【Rails】 正規表現を使用して文字列からほしい文字を取得する!! 【gsubメソッド】【sliceメソッド】【scanメソッド】【最短マッチ】

[前回の記事](https://qiita.com/mm_st/items/3c613bdce6f18594355c)で取得したツイートから必要な内容を取得したい!!!と思い正規表現についてググったものをまとめていきます:)

# 正規表現
>正規表現(regular expression)は文字列のパターンを記述するための言語です。また、この言語で記述されたパターンも正規表現と呼びます。
正規表現を用いると、文字列が指定したパターンを含んでいるかどうかを判定し、また含んでいるならばそれが文字列中のどの場所であるかを知ることができます。

[Ruby 3.0.0 リファレンスマニュアル > 正規表現](https://docs.ruby-lang.org/ja/latest/doc/spec=2fregexp.html)

# 今回取得したツイート
このツイートから***値段*** ***ブランド*** ***アイテム名*** を取得したい!
> 【GUCCI】
インターロッキングGシルバー
¥40,700- tax in

>【Lansing store】
アロマキャンドル（4個）
¥3,000-前後 参考価格
★フレンチバニラ
★ガーデニア

>【JBL】
Pulse 4
¥21,780- tax in


# gsubメソッド
>文字列中で pattern にマッチする部分全てを文字列 replace で置き換えた文字列を生成して返します。

[Ruby 3.0.0 リファレンスマニュアル > gsub](https://docs.ruby-lang.org/ja/latest/method/String/i/gsub.html)

このメソッドを使用して、ツイートの段落部分を無くす。

```
# textにツイート内容を代入する
text = "【GUCCI】
インターロッキングGシルバー
¥40,700- tax in

【Lansing store】
アロマキャンドル（4個）
¥3,000-前後 参考価格
★フレンチバニラ
★ガーデニア

【JBL】
Pulse 4
¥21,780- tax in"
=> "【GUCCI】\nインターロッキングGシルバー\n¥40,700- tax in\n\n【Lansing store】\nアロマキャンドル（4個）\n¥3,000-前後 参考価格\n★フレンチバニラ\n★ガーデニア\n\n【JBL】\nPulse 4\n¥21,780- tax in"

# gsubメソッドを使用して段落をなくす
irb(main):014:0> text_content = text.gsub(/[\r\n]/,"")
=> "【GUCCI】インターロッキングGシルバー¥40,700- tax in【Lansing store】アロマキャンドル（4個）¥3,000-前後 参考価格★フレンチバニラ★ガーデニア【JBL】Pulse 4¥21,780- tax in"
```



# sliceメソッド
> 指定された自身の要素を返します。Array#[] と同じです。
[Ruby 3.0.0 リファレンスマニュアル > slice](https://docs.ruby-lang.org/ja/latest/method/Array/i/slice.html)

```
irb(main):001:0> a = "(111)と【222】"
=> "(111)と【222】"
irb(main):002:0> a.slice(/(?<=\().*?(?=\))/)
=> "111"
irb(main):003:0> a.slice(/(?<=\【).*?(?=\】)/)
=> "222"
```
このような形で取得することができる。

aにはこのように文字列が代入されている。
`a = "(111)と【222】"`

`slice.(/(?<=\().*?(?=\))/)`で`()`内の文字を取得している

`slice.(/(?<=\【).*?(?=\】)/)`で`【】`内の文字を取得している

これでいける!!と思ったが複数該当するものがあるとうまくいかないことに気づいた...

`前提としてtweet_contentまで代入しておく`

```
# textにツイート内容を代入する
irb(main):001:0> text = "【GUCCI】
インターロッキングGシルバー
¥40,700- tax in

【Lansing store】
アロマキャンドル（4個）
¥3,000-前後 参考価格
★フレンチバニラ
★ガーデニア

【JBL】
Pulse 4
¥21,780- tax in"
=> "【GUCCI】\nインターロッキングGシルバー\n¥40,700- tax in\n\n【Lansing store】\nアロマキャンドル（4個）\n¥3,000-前後 参考価格\n★フレンチバニラ\n★ガーデニア\n\n【JBL】\nPulse 4\n¥21,780- tax in"


# gsubメソッドを使用して段落をなくす
irb(main):014:0> text_content = text.gsub(/[\r\n]/,"")
=> "【GUCCI】インターロッキングGシルバー¥40,700- tax in【Lansing store】アロマキャンドル（4個）¥3,000-前後 参考価格★フレンチバニラ★ガーデニア【JBL】Pulse 4¥21,780- tax in"
```

`slice(/¥.*-/)`を使用して`¥`と`-`の間の文字を取得する

```
irb(main):015:0> price = text_content.slice(/¥.*-/)
=> "¥40,700- tax in【Lansing store】アロマキャンドル（4個）¥3,000-前後 参考価格★フレンチバニラ★ガーデニア【JBL】Pulse 4¥21,780-"
```

`slice(/(?<=\【).*?(?=\】)/)`を使用して`【`と`】`の間の文字を取得する

```
irb(main):016:0> brand = text_content.slice(/(?<=\【).*?(?=\】)/)
=> "GUCCI"
```

`slice(/(?<=\】).*?(?=\¥)/)`を使用して`】`と`¥`の間の文字を取得する

```
irb(main):017:0> item = text_content.slice(/(?<=\】).*?(?=\¥)/)
=> "インターロッキングGシルバー"
```

`結果`

・複数当てはまってしまう部分があり、うまくいかない箇所がある
・**値段**がうまく取得できていない


# scanメソッド
>self に対して pattern を繰り返しマッチし、マッチした部分文字列の配列を返します。

>pattern が正規表現で括弧を含む場合は、括弧で括られたパターンにマッチした部分文字列の配列の配列を返します。


[Ruby 3.0.0 リファレンスマニュアル > scan](https://docs.ruby-lang.org/ja/latest/method/String/i/scan.html)

該当している文字を複数個取得したいので**scanメソッド**使用してみる

```
#　前提としてtext_contentに代入してあるものとする
irb(main):044:0> text_content
=> "【GUCCI】インターロッキングGシルバー¥40,700- tax in【Lansing store】アロマキャンドル（4個）¥3,000-前後 参考価格★フレンチバニラ★ガーデニア【JBL】Pulse 4¥21,780- tax in"
```
`scan(/¥.*-/)`を使用して`¥`と`-`の間の文字を取得する

```
irb(main):045:0> price = text_content.scan(/¥.*-/)
=> ["¥40,700- tax in【Lansing store】アロマキャンドル（4個）¥3,000-前後 参考価格★フレンチバニラ★ガーデニア【JBL】Pulse 4¥21,780-"]
```
`scan(/(?<=\【).*?(?=\】)/)`を使用して`【`と`】`の間の文字を取得する

```
irb(main):046:0> brand = text_content.scan(/(?<=\【).*?(?=\】)/)
=> ["GUCCI", "Lansing store", "JBL"]
```

`scan(/(?<=\】).*?(?=\¥)/)`を使用して`】`と`¥`の間の文字を取得する

```
irb(main):047:0> item = text_content.scan(/(?<=\】).*?(?=\¥)/)
=> ["インターロッキングGシルバー", "アロマキャンドル（4個）", "Pulse 4"]
```

`結果`
price (値段)だけうまく取得できない

# 正規表現の最短マッチを使用する
最短マッチについてはこちらを参考にさせていただきました!

[これで分かった！最短マッチ - Qiita](https://qiita.com/pyon_kiti_jp/items/15d35e7e70d09e6e9b60)

`問題のある部分`

`scan(/¥.*-/)`を使用して`¥`と`-`の間の文字を取得する

```
irb(main):045:0> price = text_content.scan(/¥.*-/)
=> ["¥40,700- tax in【Lansing store】アロマキャンドル（4個）¥3,000-前後 参考価格★フレンチバニラ★ガーデニア【JBL】Pulse 4¥21,780-"]
```

##1.「＋」と「＊」の違いについて

`*`から`+`に変更する

`*`は０文字以上でマッチするのでこの形でもマッチしてしまう。
値段がない時は取得したくない。

```
irb(main):064:0> text = "インターロッキングGシルバー¥- tax in"
=> "インターロッキングGシルバー¥- tax in"
irb(main):065:0> price = text.scan(/¥.*-/)
=> ["¥-"]
```

`+`は1文字以上でマッチするのでこの形でこのようになる。
今回はこっちを採用する!

```
irb(main):067:0> text = "インターロッキングGシルバー¥- tax in"
=> "インターロッキングGシルバー¥- tax in"
irb(main):069:0> price = text.scan(/¥.+-/)
=> []
```

##2.「?」を使用する

**?**を使用することで最短マッチを使用することができる


以下のように`price = text_content.scan(/¥.+-/)`に変更しただけでは根本的な解決にはつながっていない。

```
irb(main):075:0>  text_content
=> "【GUCCI】インターロッキングGシルバー¥40,700- tax in【Lansing store】アロマキャンドル（4個）¥3,000-前後 参考価格★フレンチバニラ★ガーデニア【JBL】Pulse 4¥21,780- tax in"
irb(main):076:0> price = text_content.scan(/¥.+-/)
=> ["¥40,700- tax in【Lansing store】アロマキャンドル（4個）¥3,000-前後 参考価格★フレンチバニラ★ガーデニア【JBL】Pulse 4¥21,780-"]
```

`price = text_content.scan(/¥.+?-/)`に変更することでこのように取得することができる:)

```
irb(main):077:0> price = text_content.scan(/¥.+?-/)
=> ["¥40,700-", "¥3,000-", "¥21,780-"]
```

#結論

`+`によって
１文字以上を含んでる時にのみ文字を取得し、
`?`によって
最短マッチを使用するため**¥**と**-**が最短で挟んでいる部分の文字を取得し、
`scanメソッド`を使用したことにより
複数ある場合は複数取得可能