正規表現のまとめ

## 正規表現（Regular expression, RE）
Web Scrapingを行っていると正規表現の知識が必要となりました。そこでlinuxとpythonによる正規表現をまとめました。ここでは、標準ライブラリreを使用します。

| 特殊文字 |  意味   |   例 |
|:-------:|:------------------:|:---|
| .       | 任意の文字にマッチ。| |
| ^       | （キャレット）文字列の先頭にマッチ| csvTest.csvのとき^csvで先頭のcsvのみにマッチ  |         
| $       | 文字列の末尾にマッチ|  csvTest.csvのときcsv$で末尾のcsvのみにマッチ       |　
| *       | 直前にあるREに作用してREを0回以上繰り返したものにマッチ |   |          
| +       | 直前にあるREに作用してREを1回以上繰り返したものにマッチ |    |       
| ?       | 直前にあるREに作用してREを0 or 1回繰り返したものにマッチ。 | ab?cならac, abcとマッチ|
| {m}     | m回の正確なコピーとマッチ。| ab{3}cならabbbcとマッチする  | 
| {m, n}  | mからn個の繰り返しマッチ。| a{2,4}ならaa, aaa, aaaaとマッチする | 
|(...)| 丸括弧中のREとマッチ|
|[]| 文字の集合を指定する。|[amp]なら'a', 'm', 'p'とマッチ。<br>[0-10]なら0,1,...,10までマッチ。<br>[^a-d]だとa-d以外|
|(?=...)　　| ...に続くものだけマッチする。先読みアサーション(lookahead assertion)|Issac(?=Asimov)だと'Issac'に'Asimov'が続く場合だけ'Issac'とマッチする。|
|(?!...) | ...に続くものとマッチしなければマッチする。否定先読みアサーション(negative lookahead assertion)|上の例のIssac(?!Asimov)だとマッチしない。|
|(?<=...)|...の後から出てくるものとマッチする。<br>後読みアサーション(lookbehind assertion)|(?<=bar)fooだとbarfooのときマッチ。|
|(?<!...)   | 否定後読みアサーション(negative lookbehind assertion)| |

### 略記表現
| 略記法  |  同等表記   |説明|
|:-------:|:---------:|:---:|
|\d       | [0-9]|数字|
|\D	 |[^0-9]	|非数字|
|\s	|[ \f\n\r\t\v]|	空白|
|\S	|[^ \f\n\r\t\v]|非空白|
|\w	|[a-zA-Z0-9_]	|単語構成|
|\W	|[^a-zA-Z0-9_]	|非単語|
> 引用元：http://doc.mas3.net/regexp/


空白, 特殊文字、`_`, 数字とマッチする.

```shell
$ echo csv_test_123_csv.csv | grep -Po '([^\s\w]|_|[0-9])+'
_
_123_
.
```


## 例題
### 行から目的の文字列を抜き出す
sedコマンド；置換や抜き出しができる。
1. `sed - E "s/.*(抜き出したい箇所のRE).*/\1/"` :  \1でキャプチャできる。
2. `sed - E "s/置換前の文字列/置換後の文字列/g"` 

```sh
$ echo abcdefg | sed -E 's/.*(d.).*/\1/'
> de

$ echo thee best time to see thee flowers is in thee spring. | sed -E 's/thee/the/g'
> the best time to see the flowers is in the spring

$ echo '<li id="name">apple</li>' | sed -E 's/<[^>]*>//g'
> apple

```

grepコマンド；一応、行の抜き出しができる。
1. grep - E "RE" :  Linux標準の正規表現
2. grep - Po "RE" : Pオプション → PCRE(Perl Compatible Regular Expressions) Perl互換の正規表現、oオプション→ only match text

```sh
$ echo value,1.2345cm, ... | grep -Po '(?<=value,)[0-9].*?(?=cm)'
> 1.2345
```


### pythonによる先読み・後読みアサーションの例

```py
import re

# 先読み. 後ろにbarがあるfooとマッチさせたい.
re.search('foo(?=bar)', 'foobar').group(0)
> foo 

# 後読み. 前にabcがあるdefとマッチさせたい.
re.search('(?<=abc)def', 'abcdef').group(0)
> 'def'

# ハイフンに続く単語を探す。（\w は任意のUnicodeにマッチ, [a-zA-Z0-9_]と同じ）
re.search(r'(?<=-)\w+', 'spam-egg').group(0)
> 'egg'

# 否定先読み; eggとマッチしない1文字を抽出（先読みでマッチしないもの）。
re.search(r'.(?!egg)', 'spam-egg').group(0)
> 's'
# 先読みならば'-'がマッチする。

# 上記grepの例。
re.search('(?<=value,)\d.+(?=cm)', "ac, value,1.2345cm,...").group(0)
> '1.2345'

```
> 複数とマッチさせるとき`findall`

次のようなXMLがあったとします。

```formula.xml
<Formula>H</Formula> 
<Formula>He</Formula> 
<Formula>Li</Formula> 
```
単文のみならば、`re.search`で良いですが、全てマッチさせたいときは、`findall`を使います。

```python
import re
data = open(file_name, 'r').read()
re.findall('(?<=Formula>).+(?=\<)', data)
> [ 'H', 'He', 'Li']
```

### マッチオブジェクト
マッチオブジェクトのブール値は常に True である。 `match()` および `search()` はマッチがないとき None を返すので、マッチがあるか単純な if 文で判定できる。欠損値を処理できるため、上記よりこちらの方が実用的。

```py
import re
lst=[]
for i in data:
    match = re.search('(?<=Formula>).+(?=\<)', i)
    if(match):
        lst.append(match.group(0))
```

## 言語処理100本ノック
http://www.cl.ecei.tohoku.ac.jp/nlp100/
第3章正規表現を一部やってみました。

```sh
#!/bin/sh
# 正規表現
wget "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/jawiki-country.json.gz"
gzip jawiki-country.json.gz
 
# 例題20 JSONデータの読み込み
cat jawiki-country.json | grep -Po '({\"text\":\s\"{{redirect|UK}).+\"title\":\s\"イギリス\"' > uk.json

# 例題21 カテゴリ名を含む行を抽出
cat uk.json | grep -Po '(Category).+]]'

# 例題22 カテゴリ名の抽出
cat uk.json | grep -Po '(?<=Category:).*?(?=[\||\]])'

```

## 参考サイト
* [サルにもわかる正規表現入門](http://www.mnet.ne.jp/~nakama/)
* [re - 正規表現操作](https://docs.python.jp/3/library/re.html)
* [手を動かしながら覚える正規表現＜基礎入門編＞](http://doc.mas3.net/regexp/)