正規表現で特定文字列を含まない方法

5月 29th, 2017

正規表現を相変わらず勉強中。

まあまあ慣れてきたのだが、困ったのが「特定文字列を含まない」ケースを一体どうするのか。特定「文字」を含まない、なら「^」が使える。でも文字列だと上手く行かない。

例えば、「ユーザー名変更」や「ユーザー名を変更」にはヒットして欲しいが、「ユーザー名を変更しない」は外して欲しい。

「(?!××)」というのがどうやらそうらしい、というので試してみた。

こんな感じ。

ユーザー名.*変更.*(?!ない)

でも、上手く行かない。他を更に調べたら、「(?!.*Value).*」みたいなのを使えとあった。Valueには外したい文字列が入る。

というわけで、改めて変更。

ユーザー名.*変更.*(?!.*ない).*

・・・やっぱり上手く行かない。ひょっとして、と()の前の「.*」を外してみた。

ユーザー名.*変更(?!.*ない).*

これで無事動いた。でも、尻尾の「.*」は、私の場合、不要な気がする。文中の一部について外したい場合用なのではないかと推定。で、更にこれも外してみた。

ユーザー名.*変更(?!.*ない)

やはりこれでも動く。いまいち理屈は分からないが(こらこら)、とりあえず動いたからいいことにしよう(こうやって誤解したまま進んでしまうんだよなあ・・・)。

タイマーを止められない

5月 27th, 2017

タイマーを実装したまでは良かった・・・が。

実際に使ってみると、「タイマーを開始したけど止めたいゾ」ということがままある。

というわけで、止める方法を探したのだが・・・

このタイマーは、「threading」を使って別スレッドを走らせている。

じゃ、別スレッド止めるか破棄すればいいじゃん。

そう単純に考えていた。

ところが。

「現状では、優先度 (priority)やスレッドグループがなく、スレッドの破壊 (destroy)、中断 (stop)、一時停止 (suspend)、復帰 (resume)、割り込み (interrupt) は行えません。」(Python 3.6.1ドキュメントより)

うそん・・・

あの手この手でなんとかしようともがいてみた。threading.timerを使うのではなく、単にカウントにしておいて、キーボードからの入力によって反応するようにとか。

上記手法は、一回目は上手く行ったけど、一回しかやらせてもらえなかった。まあ、スレッドの破壊・復帰はできませーん、とあるんだからそうなんだけど。「run()」は一回しか呼び出せない仕様なのだ。うう。

1秒ごとに信号チェックをして動作して行けば、見た目的にはきちんと動いているように見えるはずだけど…

大体っ、そもそもっ、inputがいつまででも入力待ちでぼけーっとしているのが悪い(←八つ当たり)。

後は、別個のプログラムにして操作、くらいかなあ…しかしpythonのプログラムから別プログラムへ信号を送る方法が分からない。ファイルに書き込めば、データを受け渡しできるけど…別プログラムは、1秒ごとにそれをチェックして、合図を取り込む。できれば直でやりとりしたいところ。

…何かとっても危険な香りがする。まあWebのチャットcgiいじった時は、アクセスするたびに毎回ファイルを開いちゃ読み、開いちゃ読みしてたけど。

python機能の限界に時々頭がぶつかるようになった今日この頃。でも、そこをなんとかできないのは、私の能力が低いからなんだよな…うう。やりたいことは単純なんだから、何か道があるに違いない・・・

lstripやrstripの罠(単なる勘違いともいう)

5月 26th, 2017

文字列操作をおさらいしていて、変な事例に出くわした。

—-
st=’python’
print (st.lstrip(‘typ’))
—-

これの実行結果はどうなるか。

そのままpythonになる、と思っていた。が、これは間違い。「hon」が表示される。

あれれれれ~?????えーとえーとえーと。

print (st.lstrip(‘pyt’))

なら、「hon」が返るのは分かるけど。

そう、根本的に、勘違いしていた。lstripやrstrip、stripは、フレーズに反応しているのではなく、「文字」に反応するコマンドなのだ。つまり、「typ」と「pyt」は、lstrip等々にとっては同じ意味で、とにかく「tとyとp」。順序はどーでもいい。

知らなかったよ~~~~~。初歩の文字列操作なのに。がーんがーんがーん。

で、どう動いているかというと、lstrip(‘typ’)の場合、左から文字列をチェックして、

1.tかyかpがあれば、削除
2.tでもyでもpでもなければ、処理終了

ひーーー。間違えてた。かんっぺきに間違えてた。恐ろしや。rstripは右からチェック、stripは両方からチェック。ほえ~~~。

なので、行末の「\n」を削るのに「rstrip(‘\n’)」は、文字列途中の「\n」を削る心配はないにせよ、末尾に特殊な入力をすると、おかしなことになる可能性がなきにしもあらず、ということに…

むむむ。とりあえず、まあ、いい、ことにしようかな(本当にいいのか?)

正規表現vs類語辞書 補足

5月 26th, 2017

同じネタで引っ張ってるなあと思いつつ…

試行回数を5万回に増やしてみた。

結果は、コンパイルを活用しても、結局正規表現を使わない方が早かった。

なので、正規表現を使わなくても行けるケースでは、原則、正規表現を使わずやる方がいいっぽい。

他方、例えばメールの中からメルアドと電話番号を取り出すとか、Webアドレスを取り出すとか、そういう場合は、正規表現を使わないとなかなか難しい(やってやれなくはなさそうだが)。

後、出現順序にこだわる場合も正規表現を使った方がやりやすそうではある…

予めコンパイルしてキーワードチェックしたら

5月 25th, 2017

長くなりすぎたので記事を分割。

前回の記事で、正規表現のパターンをコンパイルしない場合、結局、単純なキーワード照合なら、正規表現を使わない方が早いようだ、という結論に達した。

じゃあ、パターンをまとめてコンパイルしておいたらどうなる、というのをちょっとやってみた。

—–
#coding;UTF-8
import re
import time

patlist3=['(私|僕|彼|それ)は','君は','あれは']
st=’私は、みかんが好きです’

repat=[]
t5=time.time()
for pat in patlist3:
    p=re.compile(pat)
    repat.append(p)
for i in range(0,10000):
    for rep in repat:
        p=rep.search(st)
        if p:
            pass
t6=time.time()
print (t6-t5)

—–
初めにパターンをまずコンパイルしてリスト化。で、それを使ってstにマッチするか照合。

で、結果は…0.0156…おおっ、何か早いぞ。走らせてみたところでは、前回いちばん早かった「正規表現全くなし」の場合とあまり変わらないくらいの速度が出ている。

そうか、頻繁に使うなら、まとめてでもコンパイルした方が早いんだな。

こうやって猛スピードで走っているのを見ると、ちょっぴりCが恋しくなる・・・ってほんの少しかじっただけだけど。Cは早かった。ものすごく早かった。絶えず暴走していた(いや、アンタが暴走させたんだろう)。

ちなみに、私はPythonすら暴走させたことがある・・・。我ながら危険。

正規表現vs類語辞書

5月 25th, 2017

相変わらず正規表現周辺をうろうろ中。

私のチャットボットは、目下、類語辞書を使うことで正規表現なしでキーワード対応をしている。単に正規表現を食わず嫌いしていただけなのだが。

で、ふと思った。どっちが早いんだろう、と。

ついでに、timeで時間を計測することを覚えた(今頃かいっ)ので、今回は、datetimeではなく、timeで計測してみた。下記のpatlistの1~3がキーワードファイル。ここにあるキーワードが、それぞれstの文字列内にあるかどうかを評価させてスピードを見る。patlist1が少し特殊で、頭に「<」がついている場合のみ正規表現とみなして照合している。

----------------
import re
import time

patlist1=['<(私|僕|彼|それ)は','君は','あれは']
patlist2=['私は','僕は','君は','あれは','彼は','それは']
patlist3=['(私|僕|彼|それ)は','君は','あれは']
st='私は、みかんが好きです'

t1=time.time()
#一部正規表現を使ってチェックする場合
for i in range(0,10000):
    for pat in patlist1:
        if pat[0]==’<':
            pat=pat.lstrip('<')
            p=re.search(pat,st)
            if p:
                pass
        else:
            if pat in st:
                pass
t2=time.time()
#全く正規表現を使わない場合
for i in range(0,10000):
    for pat in patlist2:
        if pat in st:
            #print ('be')

t3=time.time()
#全て正規表現の場合
for i in range(0,10000):
    for pat in patlist3:
        p=re.search(pat,st)
        if p:
            pass
t4=time.time()
print (t2-t1)
print (t3-t2)
print (t4-t3)

ちょっと並び順が分かりにくくて申し訳ない。とりあえず結果は…

正規表現との混合:0.047
正規表現なしの時:0.015
正規表現のみの時:0.063

正規表現を扱わない方が、どうしても早い。まあ、当たり前といえば当たり前かもしれない。こういう使い方の場合、予め正規表現のパターンをコンパイルしておくことができない。

良く使うものについては、始めにコンパイルしておき、それを使えば早くなるだろうが、出現の確率が低そうな場合は、かえって手間の方が増えそうな気がする。

正規表現が使える方が便利は便利なのだが・・・たとえば、文末の「そうだ」系に反応させたい時、「そうだね」「そうだな」等々一つ一つ類語辞書を作ってもいいが、これだと「そうだの」とか「そうだぬ」とか、ちょっと変わった表現を使う人相手だと反応しなくなってしまう。ここを正規表現で「そうだ.?$」にすれば、その辺りはクリアできる。

うううん・・・後、そもそも「まとめてコンパイルしておく」方法ってどうやるんだろうというのが…リスト化とかできるのかな??

正規表現コンパイルする?しない?

5月 24th, 2017

pythonの正規表現は、予めパターンをコンパイル(機械が理解できる機械語に翻訳すること)しておける。何度もそのパターンを使って一致するかチェックしたい場合は、これをしておくと早くなる。

・・・というわけで、ちょいと実験してみた。後、正規パターンの指示は、変数に入れておけるかもついでに実験。

#coding:UTF-8

import re
import datetime

pat='よろしく.?$'

slist=['よろしくね','そんなこんなで、よろしく','よろしくお願いします','困ったらよろしく','ではでは','よろしくな']

t1=datetime.datetime.today()

#コンパイルあり
p=re.compile(pat)
for i in range(0,10000):
    for st in slist:
        m=p.search(st)

t2=datetime.datetime.today()

#コンパイルなし
for i in range(0,10000):
    for st in slist:
        m=re.search(pat,st)

t3=datetime.datetime.today()

print(t2-t1)
print(t3-t2)

じゃじゃーん。正規表現のマッチングパターン(というのかな)は、変数に放り込んでおけることが分かった。ってことは、ファイルに記入しておいて、それを読み込み、次々当てはめてチェック、という技が使える。よしよし。

で、肝心の速度だが、結構な差が出た。コンパイルなしの方が二倍くらい時間がかかっている・・・といっても1秒未満の世界だが。私の環境だとコンパイルありが0.06秒強、なしが0.12秒強といったところだった。

小数点以下を四捨五入して整数部だけ表示したい

5月 23rd, 2017

タイマーをつけたはいいけど、残り時間が見えないと使っていて不便。

というわけで、後何秒か尋ねると教えてくれるようにした。けど・・・うううん。

datetimeで時間を取得し、total_seconds()で差を秒数に変えると、小数点以下がずらずらと出て来てしまう。float型で取得するので、そのままではstr型データと混ぜて表示ができない。

とりあえず例えば、
sa=4.3908

みたいな数字があるとして、小数点以下四捨五入は、roundでできる。後ろの「0」を1や2や・・・と変えれば、どの桁で四捨五入するかを変更できる。今回は、小数点以下は不要なので、

n=round(sa,0)

とした。これで小数点以下一桁を四捨五入してくれる。ところが、このnの中身は、4.0。・・・あーえーうーー、その小数点以下の「0」は不要なんだけど、python君。

「残り4.0秒です」

とか答えて欲しくない。で、仕方が無いので、int化→更にstr化することにした。

print (‘残り時間は、’+str(int(n))+’秒です’)

これで、「残り時間は、4秒です」と答えてくれるようになった。

いまいち格好よろしくないような気もするけど、まあ、いいかあ・・・

正規表現とどっちが早い?

5月 22nd, 2017

ずるずると正規表現を勉強中。

正規表現はいろいろできるけど、遅い、という話を良く見る。どのくらいなのか、末尾一致チェックの方法を覚えたので、ちょっと試してみた。

単純に1対1だと、圧倒的にendswithを使う方が早い。単にチェックさせるだけなら、1,000やそこらチェックしてもdatetimeを使った計測では所要時間が見えないくらいに早い。

ただ、正規表現の柔軟性が活躍できる場面だと、だんだん逆転してくる。あまり良いサンプルではないかもしれないが、とりあえず、下記を走らせてみた。

#codint:UTF-8

import re
import datetime

slist=['よろしくね','そんなこんなで、よろしく','よろしくお願いします','困ったらよろしく','ではでは','よろしくな']

t1=datetime.datetime.today()
p=re.compile('よろしく.?$')
for i in range (0,10000):
    for st in slist:
        m=p.search(st)
t2=datetime.datetime.today()
sa1=t2-t1

t1=datetime.datetime.today()
for i in range (0,10000):
    for st in slist:
        j=st.endswith('よろしく')
        k=st.endswith('よろしくね')
        l=st.endswith('よろしくな')
t2=datetime.datetime.today()
sa2=t2-t1

print (sa1)
print (sa2)

slist中身をマッチング×10,000回。ただ、マッチングだけをさせているので、何の役にも立たない仕事をさせているが…それはさておき。

この辺りになってくると、正規表現の方がだんだん有利になってくる。「よろしく!」等にもまとめてマッチさせたい場合は、断然正規表現を使った方が楽だ。endswithだと、全て一つ一つ確認して追加する必要がある。

ただ、他方、正規表現だと、思わぬものがマッチする可能性がある。

。。。悩ましい・・・

文字列の頭や尻尾をチェックする

5月 21st, 2017

文字列が特定のもので始まっているのか、あるいは終わっているのかを確認する方法ってないのかなあ、と思っていたら、普通にあった。

「すたーとうぃず」と「えんどうぃず」。でも、サンプル通りタイプしても何故かエラーが出て上手く行かない。

何度やっても上手く行かないので、コピペしてみた・・・ら動いた。

一体これはどういうことだ???

もう一度よーーーーーく見てみたら、「すたーとうぃず」は「startwith」ではなく「startswith」だった。「えんどうぃず」も同様に、「endwith」ではなく、「endswith」だった。

まさかの複数形。うわい。なので「すたーつうぃず」であり「えんずうぃず」なのだった。

使い方は難しくない。

st=’abcdefg’
st.endswith(‘fg’)

上記なら、’True’が返る。二行目を
st.endswith(‘k’)
にすれば、Falseが返る。後、後ろに開始位置と終了位置を指定できる。

でもこの位置指定、苦手なんだよなあ…

上記stの場合、

st.endswith(‘d’,0,4)
でTrueになる。0,3ではダメ。aが0番なんだからdは3番だ、とつい直感的に考えてしまう。で、間違える。

……割と初期に出ているはずのこのコマンドが意識から飛んでいたので、キーワードマッチの仕様を変更しないとね…文末一致が使えるようにせねば。うう。うれしいといえばうれしいけど、もうキーワードマッチの部分、見たくないよう