デカ文字Python

デカ文字&短文でPythonを簡潔にまとめたブログ

Python SoloLearn Regular Expressions②

f:id:pokita:20181013095615p:plain

Metacharacterという、いわゆる特殊文字についての内容。
個人的には表記が汚く思えるため、読みづらいし好きではない。

 

正規表現r""の中では特殊文字も全て効力無効化にしてくれ

 

と思うのはぼくだけではないはず…たぶん。

 

 

Metacharacters

特殊文字(Metacharacters)

Metacharacters allow you to create regular expressions to represent concepts like "one or more repetitions of a vowel".

 

先にその特殊文字の一例 

文字 説明
. デフォルトのモードでは改行以外の任意の文字にマッチします。 DOTALL フラグが指定されていれば改行も含むすべての文字にマッチします。
^ 文字列の先頭とマッチします。 MULTILINE モードでは各改行の直後にマッチします。
$ 文字列の末尾、あるいは文字列の末尾の改行の直前にマッチします。例えば、 foo は 'foo' と 'foobar' の両方にマッチします。一方、正規表現 foo$ は 'foo' だけとマッチします。興味深いことに、 'foo1\nfoo2\n' を foo.$ で検索した場合、通常のモードでは 'foo2' だけにマッチし、 MULTILINE モードでは 'foo1' にもマッチします。 $ だけで 'foo\n' を検索した場合、2箇所 (内容は空) でマッチします: 1つは、改行の直前で、もう1つは、文字列の最後です。
* 直前にある RE に作用して、 RE を 0 回以上できるだけ多く繰り返したものにマッチさせるようにします。例えば ab* は 'a'、'ab'、あるいは 'a' に任意個数の’b' を続けたものにマッチします。
+ 直前にある RE に作用して、 RE を、1 回以上繰り返したものにマッチさせるようにします。例えば ab+ は 'a' に一つ以上の 'b' が続いたものにマッチし、 'a' 単体にはマッチしません。
? 直前にある RE に作用して、 RE を 0 回か 1 回繰り返したものにマッチさせるようにします。例えば ab? は 'a' あるいは 'ab' にマッチします。

ここで重要なのが、以下の3点

特殊文字はあくまでも正規表現(re関係)内だけで働く
b = r"aa?"
c = "aak"

print
(b == c)

>>>False

reモジュールを使っていない、純粋のstring型の式においては"?"は"文字?"でしかない。そのため、文字列"aa?"と文字列"aak"は一致しない。

 

特殊文字はパターン(reモジュール関数内の第一引数)でのみ効果を発揮する
pattern = "aa."
source = "aak"

print(re.match(pattern, source))

>>><_sre.SRE_Match object; span=(0, 3), match='aak'>

print(re.match(source, pattern))

>>>None

.に「任意の一文字」という風な振る舞いをさせるためには、re.match関数の第一引数に入れなければいけない。

 

特殊文字を世間一般のワイルドカードと混同してはいけない。
import re

source = "Young pokita_kroger"

m = re.match("pokita", source)
print(m)
>>>None

s = re.search("pokita", source)
print(s)
>>><_sre.SRE_Match object; span=(6, 12), match='pokita'>

m_M = re.match(".*pokita", source)
print(m_M)
>>><_sre.SRE_Match object; span=(0, 12), match='Young pokita'>

match関数はpatternがsourceの先頭から一致しているものについてのみ、matchオブジェクトを戻すため、Noneを戻す。

serch関数はpatternの文字列がsource内に存在さえしていれば、searchオブジェクトを戻すため、searchオブジェクトspan(6,12)を戻す。

特殊文字”.*"は任意の文字の任意の繰り返しを表すため、patternは"Young pokita"となり、searchオブジェクトspan(0,12)を戻す。

 

ん?任意の文字の任意の繰り返しって、ワイルドカード*だけで充分では?

 

やってみる。

import re

source = "Young pokita_kroger"

m_M = re.match("*pokita", source)
print(m_M)
>>>Traceback (most recent call last):
File "C:/Users/pokio/PycharmProjects/hazime/hatena.py", line 5, in <module>
m_M = re.match("*pokita", source)
File "C:\Users\pokio\AppData\Local\Programs\Python\Python36\lib\re.py", line 172, in match
return _compile(pattern, flags).match(string)
File "C:\Users\pokio\AppData\Local\Programs\Python\Python36\lib\re.py", line 301, in _compile
p = sre_compile.compile(pattern, flags)
File "C:\Users\pokio\AppData\Local\Programs\Python\Python36\lib\sre_compile.py", line 562, in compile
p = sre_parse.parse(p, flags)
File "C:\Users\pokio\AppData\Local\Programs\Python\Python36\lib\sre_parse.py", line 855, in parse
p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, 0)
File "C:\Users\pokio\AppData\Local\Programs\Python\Python36\lib\sre_parse.py", line 416, in _parse_sub
not nested and not items))
File "C:\Users\pokio\AppData\Local\Programs\Python\Python36\lib\sre_parse.py", line 616, in _parse
source.tell() - here + len(this))
sre_constants.error: nothing to repeat at position 0

画面真っ赤すぎワロタ…ワロタ…

 

世間一般にいうところのワイルドカードと、Python特殊文字を混同していたため、ワイルドカード"*"で済むことを、なぜPythonでは".*"と書かなければいけないのか、と躓いてました。

 

そりゃそうでしょ。Python特殊文字ワイルドカードはまったくの別もんだもの

 

と今になっては思うけど、でもネットで検索してみると結構な数のサイトでこの誤用が見られた。

それに紛らわしいのが、Pythonコマンドラインで使う際は通常通りワイルドカードが使えるため、こんな記事も溢れてたこと。

 

 

検索技術がないことを棚に上げた言いがかりでしょ

 

はい…すみません。
調べる際は横着せず公式のドキュメント見るようにします。。。

 

6.2. re — 正規表現操作 — Python 3.6.5 ドキュメント

 

Character Classes

Character classes provide a way to match only one of a specific set of characters.
A character class is created by putting the characters it matches inside square brackets.

 

Character Class?また新しいの出てきたな(警戒)

 

とならずに。これも特殊文字のひとつ。
reモジュールの関数で使う引数patternの便利な書き方の一種。

 

 まとめるとこんなところ。

文字 説明
[]

文字の集合を指定するのに使用します。集合には以下のものが指定できます:

  • 個別に指定できる文字。 [amk] は 'a''m''k' とマッチします。
  • 連続した文字の範囲を、先頭と最後の2文字とその間に '-' を挟んだ形で指定できます。[a-z] はすべての小文字の ASCII 文字とマッチします。[0-5][0-9] は 00 から 59 までの、すべての 2 桁の数字とマッチします。[0-9A-Fa-f] は任意の 16 進数の数字とマッチします。-が、エスケープされた場合 (例: [a\-z])、あるいは先頭か末尾に置かれた場合 (例: [-a] や [a-])、リテラル '-' とマッチします。
  • 集合内では、特殊文字はその意味を失います。 [(+*)] はリテラル文字 '(' '+' 、 '*' 、あるいは ')' のいずれかとマッチします。
  • \w や \S のような文字クラス (後述) も集合内に指定できますが、それらにマッチする文字は ASCII か LOCALE のどちらか有効にされているモードに依存します。
  • 範囲内にない文字とは、その集合の 補集合 をとることでマッチできます。集合の最初の文字が '^' の時、集合に ない 文字すべてとマッチします。 [^5] は '5' を除くあらゆる文字にマッチします。 [^^] は '^' を除くあらゆる文字にマッチします。 ^ は集合の最初の文字でない限り特別の意味を持ちません。
  • 集合内でリテラル ']' をマッチさせるには、その前にバックスラッシュをつけるか、集合の先頭に置きます。 [()[\]{}] と []()[{}] はどちらも ']' にマッチします。
import re

pattern = r"[aeiou]"
s = re.search(pattern, "grey")

print(s)
>>><_sre.SRE_Match object; span=(2, 3), match='e'>

source(第二引数)に母音が含まれていないか、を判定するコード。

 

More Metacharacters

The metacharacter * means "zero or more repetition of previous thing".
The "previous thing" can be a single character, a class, or group of characters in parentheses. 

import re

pattern = r"chapter[1-9]*"
m = re.match(pattern, "chapter11")

print
(m)
>>><_sre.SRE_Match object; span=(0, 9), match='chapter11'>

特殊文字*がclass[1-9]に反応してchapter11と一致した。
ではchapter1-chapter9と一致させたい場合は?

→繰り返しさせたい文字列を()で囲みグループ化する。

import re

pattern = r"(chapter[1-9](-)?)*"

m = re.match(pattern, "chapter1-chapter9")
print(m)
>>><_sre.SRE_Match object; span=(0, 17), match='chapter1-chapter9'>

 

 Special Sequences

6.2. re — 正規表現操作 — Python 3.6.5 ドキュメント

 

にはその他使える特殊文字たちの説明がある。
例えば、文中に含まれている単語”I"の数が知りたいとき。

import re

source ='''I wish I may, I wish I might,
...... Ignore what I said tonight.'''

m = re.findall(r"\bI\b", source)

print(len(m))
>>>5

"I"の前後は文字ではない、という条件からr"\WI\W"を使いたいが、これでは"I wish…"で始まる文であることから、文頭のIを拾い忘れてしまう。そのため、文字以外はもとより、文頭および文尻を拾うことができるr"\bI\b"を使う。

 

今回はここまで。