Samuel Johnsonの言葉である。
「言語は国家の系譜なのだ。」
その国の言語の成り立ちをみればその国がどのように形成されたが分るという意味である。
例えばルーマニア。中央ヨーロッパで唯一ラテン語系の言語を話す国である。ローマ帝国の影響を色濃く受けた結果である。
Samuel Johnsonの言葉である。
「言語は国家の系譜なのだ。」
その国の言語の成り立ちをみればその国がどのように形成されたが分るという意味である。
例えばルーマニア。中央ヨーロッパで唯一ラテン語系の言語を話す国である。ローマ帝国の影響を色濃く受けた結果である。
Newscientistのあった記事のタイトルである。
一般的に野生動物の脳に比較して家畜化された動物の脳は小さい。これはウシのばあいも同じか?家畜化されたウシの祖先は絶滅しまっていてこのことは不明であった。
チューリッヒ大学の研究者たちは化石として残っているオーロックス,(Bos primigenius)の13個の頭蓋骨の大きさを家畜のウシ (Bos taurus)71品種317個の頭蓋骨の大きさと比較した。
頭蓋骨の大きさの比較から研究者たちはウシの脳はオーロックスに較べて26パーセントも小さいと推定した。しかもウシの間でも差異があり、よりヒトとのコンタクトが大きい乳牛の脳はよりコンタクトの小さい闘牛と比較して小さいことも明らかになった。ヒトに従順になるの従って脳の大きさが小さくなるようの思える結果である。
宇宙である瞬間に起きている素過程は至るところで同じであるとすると考えてみよう。これを超並列系と呼ぶことにする。
このような宇宙で、あるところでは生命体が誕生するし、あるところでは銀河も星も誕生しない。
それは何故か?それは進化する宇宙の初期にゆらぎがあったからだ。宇宙の起きている素過程がこのゆらぎを増幅した結果が銀河、星の誕生であるし、生命体の誕生である。
正規表現は元々ASCIIコードの文字列を念頭に置いた処理であったがUnicodeへの拡張がされている。
>>> print(re.findall(r'\d+','2021年6月十五日'))
['2021', '6']
漢数字にはマッチしない。半角数字のみでは[0-9]の範囲指定、全角数字のみでは[0-9]の範囲指定を使う。
>>> print(re.findall(r'[あ-ん]+','雨ニモ負ケズ、風にも負けず'))
['にも', 'けず']
>>> print(re.findall(r'[ア-ン]+','雨ニモ負ケズ、風にも負けず'))
['ニモ', 'ケズ']
注)Unicode表に沿えばひらがなの範囲は[ぁ(小書きのあ)-ゖ(小書きのけ)]であるが伝統的にはひらがなは「あ」で始まり「ん」で終わるべきだ。
ひらがなカタカナをマッチングから外すには補集合表示が使える。
>>> print(re.findall(r'[^あ-んア-ン]+','雨ニモ負ケズ、風にも負けず'))
['雨', '負', '、風', '負']
>>> print(re.findall(r'.っ','はっきり、つめる'))
['はっ']
>>> print(re.findall(r'.つ','はっきり、つめる'))
['、つ']
>>> print(re.findall(r' .','これは 全角、これは 半角'))
[' 半']
>>> print(re.findall(r' .','これは 全角、これは 半角'))
['\u3000全']
>>> print(re.findall(r'.。|.、','おお、寒い。'))
['お、', 'い。']
以上、Unicodeの扱いを纏めてみた。
正規表現ではバックスラッシュ(\)が多用される。一方Pythonの文字列ではこのバックスラッシュが特別な意味を持つことがある。例を挙げる:
>>> len('\section')
8
>>> len('\next')
4
>>>
文字列でバックスラッシュに続けてnがあるとそれらの2個で特別な意味を持つ1文字となる。だから文字列’\section’では文字数が8文字であることと対照的に文字列’\next’は4文字となる。この特別な意味を持つ1文字をエスケープシーケンスと呼ぶ(なぜエスケープシーケンスと呼ぶかは最初<Esc>キーをエスケープ文字にした歴史的な経緯による)。
正規表現もPython文字列である。しかもバックスラッシュが多用される。そのバックスラッシュがエスケープ文字として働くと厄介なことが起きる。
これを回避する一つの方法が通常の文字列の代わりにraw_stringを用いるものである。この記法は通常の文字列の前にrを付ける。実例を示す:
>>> len(r'\next')
5
>>>
この記法ではエスケープシーケンスはその特別な意味を失い、単なる普通の二文字となる。
この記法で書けばエスケープシーケンスが紛れ込むことを気にしないで正規表現を記述できる。例を示す:
#coding: utf-8
import re
text = r'次の節は\nextで始める'
m = re.search(r'\\next', text)
print(m.group(0))
正規表現の中の\\は正規表現の特別文字、バックスラッシュを普通の文字とするためである。
正規表現でマッチした文字列の全てに興味がある訳ではないことがしばしば起こる。例えば固定電話番号のマッチングでは市外局番・市内局番・加入者番号がマッチすることを要求するが興味があるのは加入者番号だけであるというようなばあいである。このばあいはマッチした文字列全体ではなく、加入者番号のみを表示すれば足りる。このようなばあいにグループ化が使える。
グループ化は(正規表現)とグループ化したい正規表現の部分を丸括弧()で括ってやる。グループ化は入れ子(表現1(表現2))でも直列(表現1)(表現2)でも構わない。グループには番号が振られる。正規表現全体はグループ0、正規表現を右に検索し左丸括弧に出会うと番号はインクリメントし、対応する右丸括弧までがこのグループに所属することになる。実例を示す:
#coding: utf-8
import re
telNumber = re.compile(r"""
^0 #ゼロ発信
(
#固定電話(市外・市内・加入者)
[36]-\d{4}- (\d{4}) #東京(3)・大阪(6)
| \d{2}-\d{3}- (\d{4}) #仙台(22)
| \d{3}-\d{2}- (\d{4]) #松本(263)
| \d{4}-\d- (\d{4}) #伊豆大島(4992)
#携帯電話(キャリア・管轄・加入者)
| [7-9]0-\d{3} (\d-\d{4})
)
""", re.VERBOSE)
m=telNumber.search('022-345-9876')
if m :
print(m.groups())
else:
print(m)
この例では正規表現に六個のグループが使われている。メソッドgroups()はこの六個のグループに対応する表示をタプルの形で取り出すことができる。この実例の結果は
('22-345-9876', None, '9876', None, None, None)
グループ1とグループ3でマッチする表現があることが分る。Noneは該当するグループにマッチする文字列がなかったことを意味する。
このグループ化のPython拡張としてグループに名前を付けることができる。それには(?P<名前>…)というふうに左丸括弧の次に?P<名前>を挿入する。実例を示す:
#coding: utf-8
import re
telNumber = re.compile(r"""
^0 #ゼロ発信
(?P<電話番号>
#固定電話(市外・市内・加入者)
[36]-\d{4}- (?P<固定0>\d{4}) #東京(3)・大阪(6)
| \d{2}-\d{3}- (?P<固定1>\d{4}) #仙台(22)
| \d{3}-\d{2}- (?P<固定2>\d{4]) #松本(263)
| \d{4}-\d- (?P<固定3>\d{4}) #伊豆大島(4992)
#携帯電話(キャリア・管轄・加入者)
| [7-9]0-\d{3} (?P<携帯>\d-\d{4})
)
""", re.VERBOSE)
m=telNumber.search('070-2345-9876')
if m :
print(m.groupdict())
else:
print(m)
メソッドgroupdict()によってグループ名をキーとしマッチした文字列を値とする辞書で結果を取り出すことができる。結果は
{'電話番号': '70-2345-9876','固定0': None,'固定1': None,
'固定2': None, '固定3': None, '携帯': '5-9876'}
となり、この電話番号は携帯で加入者番号が5-9876であることが表示される。
正規表現はとかく呪文のような読み難い表現になり勝ちであるごが、Pythonでは少し工夫がされていて冗長な表現ができる。実例を示す:
import re
telNumber = re.compile(r"""
^0 #ゼロ発信
(
#固定電話(市外・市内・加入者)
[36]-\d{4}- (\d{4}) #東京(3)・大阪(6)
| \d{2}-\d{3}- (\d{4}) #仙台(22)
| \d{3}-\d{2}- (\d{4]) #松本(263)
| \d{4}-\d- (\d{4}) #伊豆大島(4992)
#携帯電話(キャリア・管轄・加入者)
| [7-9]0-\d{3} (\d-\d{4})
)
""", re.VERBOSE)
m=telNumber.search('070-2345-9876')
if m :
print(m.group(0))
else:
print(m)
日本の電話番号のマッチングのための正規表現であるが、コメント(#)や空白・改行を使って見やすくできる。
前のブログの続きである。
正規表現上でキャレットは前述の二つの位置以外では特別な意味は持たないが特殊記号にはちがいない。従って単なる文字としてキャレットとマッチしたいときには
\^
とバックスラッシュ(\)を使う。
[^^]
これは先頭のキャレットが補集合キャレットで二番目のキャレットは単なる文字としてのキャレットである。和集合のなかでは補集合キャレットを例外として全ての特殊記号は単なる文字として振舞う。上の正規表現はキャレットでない任意の一文字でマッチする。