アルファベットが含まれない文字列を判定


Google Homeアプリを作成中のこと。医薬品マスタからクレストール錠5ミリをヒットさせたい。そこで「クレストールの錠剤、5ミリ」とGoogle Homeを話しかけたが、「クレストール錠5ミリ」と「クレストールOD錠5ミリ」の2つがヒットしてしまう。OD錠じゃない方だけを返してもらいたいので、いろいろ調べてみた。

最初にやったこと

単に名前が短い方を選ぶようにした。

medicine_names = [u'クレストール錠5ミリ', u'クレストールOD錠5ミリ']
shorter_name = min(medicine_names, key=len)

このコードだとなにが流れてきても短いほうが選ばれてしまう。例えばmedicine_names = [u'クレストール錠2.5ミリ', u'クレストール錠5ミリ']の場合、クレストール錠5ミリがヒットすることになり、アルファベットが含まれない文字列を判定したいという意図を反映しない。

どうするか?

やることは2つ。文字列の差分をとること。その差分がアルファベットかどうかを判定すること。

文字列の差分を取得する

list(set(list_a) - set(list_b))とすると、list_aのうちlist_bに含まれないものだけがリストで返ってくる。具体的には・・・

medicine_names = [u'クレストール錠5ミリ', u'クレストールOD錠5ミリ']
medicine_a, medicine_b = [list(m) for m in medicine_names].sort(key=len)
diff = ('').join(list(set(medicine_b) - set(medicine_a)))

アルファベットかどうかを判定する

正規表現に手を出したくなるが、文字列メソッドの.isalpha()で一撃。

diff = ('').join(list(set(medicine_b) - set(medicine_a)))
print diff
# u'OD'
diff.isalpha()
# True

おまけ

文字コードや全角半角でTrue/Falseが変わるかを検証。python2.7.10。

a = u'OD' # 半角
b = 'OD' # 半角
a.isalpha() # True
b.isalpha() # True

c = u'OD' # 全角
d = 'OD' # 全角
c.isalpha() # True
d.isalpha() # False

e = d.decode('utf-8') # utf-8 -> unicode
e.isalpha() # True

f = e.encode('utf-8') # unicode -> utf-8
f.isalpha() # False

半角だと文字コードにかかわらずTrue判定。全角はunicodeのみTrue判定。理屈はわかりませんが、[修正] Python 文字列の英数字判定でハマったのコメントが参考になるかもしれない。

参考

文字列処理メソッドのまとめ
Python で差分リストを取得する
[修正] Python 文字列の英数字判定でハマった