IT・技術研修ならCTC教育サービス

サイト内検索 企業情報 サイトマップ

研修コース検索

コラム

ゼロから歩くPythonの道

CTC 教育サービス

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  Microsoft Azure  Docker  Kubernetes 

第17回 エラーチェックとreモジュール~正規表現とかのアレコレ~ (菱沼佑香) 2021年2月

吉政創成 アシスタントの菱沼です。

今回も「いちばんやさしいPython入門教室(大澤 文孝氏著) 」を片手に勉強していきます。

 

さて、前回に引き続き、今回もヒット&ブローという数字当てゲームを作っていきます。

前回は4桁のランダムな数字の作り方と、リスト化まで行いました。今回は回答者側の処理部分で、まずは入力エラーの判定から学んでいきます。

 

まずは前回までのおさらい

1桁と4桁のランダムな数字の作り方はこちらです。

※1桁バージョンだけその後の動作もかかれています。(b=のところからが該当)

fig01

ランダムな数字を出すのは赤枠の部分が該当しています。

ヒット&ブローは桁ごとに正答を判断するため、その後の処理を楽にすることを考えて、桁ごとにランダムな数字を作ってあげる方式が取られています。また、値を一つの変数に入れると値をリストとして扱えるようなり、当てっこの時に処理がより楽になるそうです。水色で書いた[数字]はリストに振られるインデックス番号です。

 

len関数を使って文字数チェック!

さて、上記の後、回答に関係する処理が入っていくことになりますが、回答された内容がルールに従っているかをチェックするための処理を学びます。まずは桁数が4桁であるかを判定する処理です。

 

引用------------

P.135

まずは、4桁で入力されたということを確認してみましょう。Pythonではlen関数を使うことで、その文字列の長さを求められます。

次のように、len(b)が4ではない――すなわち、「if len(b) !=4:」という条件を指定すると、4桁で入力できたかどうかを確認できます。

 

>>> b = input(“数を入れてね>”)

>>>if len(b) ! = 4:

>>>  print(“4桁の数字を入力してください”)

 

しかし、実際には4桁であることをチェックするだけではなく、4桁でなければ4桁がきちんと入力されるまで、繰り返し入力してほしいということがほとんどだと思います。そこで、whileを使ってループ処理するようにexample05-04-01.pyを以下のように書き換えます。

-----

 

ということで、次の画像がそれにあたります。

左側はユーザーに回答を入力させるだけの部分。中央が入力された値の桁数チェック部分、右側が実行結果です。確かに桁数が足りない場合にはエラーメッセージが出力され、4桁になるまで繰り返されています。

fig02

ちなみに「len(b) ! = 4:」の「! = 」は等しくないという意味を持つ比較演算子です。Pythonで使える比較演算子はこちらの記事もご参照ください。

 

数字であるかをチェックするには?

さて次に、入力された文字がちゃんと数字であるかをチェックする方法です。

 

引用-----

P.138

各桁が数字であるということは、それぞれの桁が0以上9以下であるということです。よって次のように判定できます。

>>> if (b[0] >= “0”) and (b[0] <= “9”) :

ここで変数bのそれぞれの要素はユーザーが入力した「文字列」を切り出したもので「数字」ではありません。ですから、「”0”」とか「“9”」のように「” ”」でくくり、「文字列」として扱う点に注意してください。(中略)

さて本題は、正しく入力「されていない」ときにエラーを表示したいということです。そこでエラーを表示するには、これとは逆の条件となるよう、以下のようにします。

>>> if (b[0] < “0”) or (b[0] > “9” ) :

>>>  print(“数字ではありません”)

------

 

b[0]は一桁目の数字をさすインデックス番号を使用しているので、4桁分を用意するにはそれぞれのインデックス番号を入れたもの(b[1]、b[2]、b[3])を用意してあげることになります。そうして先ほどの4桁かのチェックと組み合わせて出来上がったのがこちら。

fig03

elseの部分から同じ記述でちょっとうっとうしい…。インデックス番号が違うだけの同じものが並んでいるので省略できそうだけど…と思ったら、やっぱりできるようです。それがこちら。

fig04

なんかプログラミングっぽい!!という感動を覚えつつ。行数はそんなに変わりませんが、書かれている内容がすっきりしています。

最初はkazuokという変数にはtrueが入っています。これは始まった時点では入力された値が「数字」であるという前提でスタートし、実際に[0]から調べてみます。順に調べていった結果、文字が入力されていることが明らかになった時点で処理は停止し、「数字ではありません」という文字が表示されます。

 

さらにすっきりしたいなら「re」モジュールを使う

一桁ずつ数字を調べるように書くのも面倒くさい場合には「正規表現」を使った「パターンマッチ」を行えばさらにすっきりできるらしいです。

 

引用-----

P.144

これをもっとスマートに書く方法として、正規表現を使うという方法があります。正規表現はパターンと呼ばれる書式を使って、文字列の頭から順に、文字がそのパターンと合致しているかどうかを調べる方法です。これをパターンマッチと言います。

数字かどうかを調べるには「\d」という特別な記号を使います。つまり先頭から「\d」が4つ繋がっているかどうかを調べることで、その文字列が4桁の数字であるかどうかを調べることができます。

------

 

さてそれが反映されたのがこちらです。

fig05

だいぶすっきりされていらっしゃる…。

 

>>> if not re.match(r”\d\d\d\d$”.b):

 

この一文だけで、4桁か、数字であるかの2つがチェックされています。

()内でそれが指定されているのだとは想像がつきますが、それぞれどんな意味があるのでしょうか。調べてみました。

 

  • raw文字列について

re.matchの後に、「r”文字列”」(「R“文字列”」でもOK)がついています。

これはraw文字列と言うそうです。これをいれるとエスケープシーケンス(¥で表現される特別な文字)を無効化するものだそうです。そのエスケープシーケンスに当たるものは次の画像のものになります。

fig06

引用:2.4.1. 文字列およびバイト列リテラル|Python公式ドキュメント

 

参考:

raw文字列の利用|Let's プログラミング

正規表現の先頭につく`r`は何ですか?エスケープシーケンスやrow文字列を解説します|Python学習チャンネル by PyQ

 

  • 正規表現について

「r」のあとに「“”」で文字列が囲まれた「¥d」が4つ書かれています。これは0~9までのどれかの数字を指す正規表現なのだそうで、4つ続いているということは4桁の10進数の数字という意味になります。

こういった正規表現には「¥(or バックスラッシュ)」が付きます。次に続く部分を「正規表現で書く」ときに、「r」を付けてあげると「\(or バックスラッシュ)」をそのままの存在として使うことができるようになるということと、わかりやすくなるということのようです。

他の正規表現についても調べたので、以下の表をご参照ください。実際に試せそうなものは試せる範囲で試してみました。

 

<特殊シーケンス>

Unicode

ASCIIの場合

説明

注意事項

\d

[0-9]

0~9の数字

全角・半角数字の判別はしない

\D

[^0-9]

数字以外の文字

全角・半角数字以外のすべての文字(記号、かな、カナ、英字等)

\s

[ \t\n\r\f\v]

空白(スペース)

全角・半角を判別しない

\S

[^ \t\n\r\f\v]

\sの反対

全角・半角の空白以外のすべての文字(記号、かな、カナ、英字等)

\w

[a-zA-Z0-9_]

ほとんどの文字、数字、アンダースコア( _ )

アンダースコア以外の記号はNG

(全角アンダースコアはNG)

\W

[^a-zA-Z0-9_]

\wの反対

半角、全角の記号(全角アンダースコアはOK)

\Z

$

文字列の末尾

 

 

<メタ文字>

記号

説明

書き方の例

例の記述で合致するもの

.

任意の一文字(改行除く)

さ.え

さざえ、さかえ、さむえ

..

任意の二文字(改行除く)

な..い

なみへい、ないがい、ないせい

^

行の先頭

^磯野

磯野なみへい、磯野ふね

$

行の末尾

$磯野

なみへい磯野、ふね磯野

*

0回以上繰り返す

いっ*つも0点

いつも0点、いっつも0点、いっっつも0点…

+

1回以上繰り返す

いっ+つも0点

いっつも0点、いっっつも0点、いっっっつも0点…

?

0回か1回

いっ?つも0点

いつも0点、いっつも0点

{m}

m回繰り返す

0点{3}

0点0点0点

{m,}

m回以上繰り返す

0点{2,}

0点0点、0点0点0点、…

{m,n}

m~n回繰り返す

0点{2,4}

0点0点、0点0点0点、0点0点0点0点

a|b

aかb

たま|ハチ

たま、ハチ

[]

[]内の値のどれか

[0-9]

0,1,2,3,4,5,6,7,8,9

[]{m}

[]内の値のどれかをm回繰り返す

[a-c]{5}

abcab,abccb,bcabaなど

 

参考:

re --- 正規表現操作|Python公式ドキュメント

pythonの正規表現メモ|Qiita @kaeruair

【Python】とっても便利な正規表現!|Qiita @hiroyuki_mrp

分かりやすいpythonの正規表現の例|Qiita @luohao0404

書きながら覚えよう!Pythonで正規表現を使う方法【初心者向け】

[Python] 正規表現の表記方法のまとめ(reモジュール)|Hbk Project

 

  • reモジュールで使える関数

次にreモジュールで使える関数はどんなものがあるかです。

 

  • re.search(検索正規表現,処理対象文字列):部分一致検索
  • re.match(検索正規表現,処理対象文字列):文字列の先頭から検索
  • re.fullmatch(検索正規表現,処理対象文字列):全体一致検索

この3つの違いについては「【python】re.match より re.search を使おう|Qiita @halhorn」がわかりやすいのでご参照ください。

 

  • re.sub(正規表現パターン,置換後の文字列,処理対象文字列,最大回数):マッチする部分を置換
  • re.subn(正規表現パターン,置換後の文字列,処理対象文字列):マッチする部分を置換

完全一致ではなく、正規表現でマッチした部分を置換するときに利用。「subn」の方を使うと、置換した数を知ることができます。

 

  • re.findall(検索正規表現,処理対象文字列):マッチした部分をリストで取得する
  • re.finditer(検索正規表現,処理対象文字列):マッチする部分をイテレータで取得

どちらも検索した結果が表示されるものですが、取得方法がリストとイテレータで異なります。それぞれの違いについて「リストとイテレータの違いについて|teratail」の回答がわかりやすかったです。以下一部抜粋しましたが、より詳しい内容はリンクをご参照ください。

<一部抜粋>

イテレータは”状態を記憶する”ので、最後の要素を呼び出したら、次の要素はいません。という状態になり、このときにnext(i)で次を呼び出せばエラー(StopIteration)になります。

これはリストでも配列数よりも大きいIndexを指定すればエラー(IndexError)になるのと同じです。

 

  • re.split(正規表現パターン,処理対象文字列,最大分割回数):正規表現パターンで文字列を分割

文字列を分割する方法は複数あるそうですが、そのなかの一つがこの関数です。具体的なイメージと、その他の分割方法は「Pythonで文字列を分割(区切り文字、改行、正規表現、文字数)|note.nkmk.me」で説明されていました。

 

  • re.compile(r”正規表現パターン文字列”,フラグ):正規表現パターンをコンパイルする

同じ正規表現パターンを繰り返して利用する場合、事前にコンパイルしておくと処理速度が速くなるそうです。flagsは省略可能で、<フラグ>の項目が入るようです。

 

<フラグ>

re.compileで利用できるフラグは全部で7種類のようです。ここでは一部だけ詳しめに書いて、他のものは関数名だけ書きます。「Pythonの正規表現で設定できるフラグの一覧|Let's プログラミング」のページで丁寧に解説されていましたので、ご参照ください。

 

  • re.ASCII(re.A)

ASCII文字のみにマッチするようになります。具体的には上表<特殊シーケンス>に記載した「ASCIIの場合」の書き方になります。(例:\d→[0-9])

ASCIIに変換されると、Unicodeの\wは「ほとんどの文字、数字、アンダースコア( _ )」にマッチングしましたが、ASCIIはa~z、A~Z、0~9になるので、ひらがななどの文字には反応しません。

\w      → OK:katsuo、KATSUO、1234、かつお  NG:!$%&

[a-zA-Z0-9_]  → OK:katsuo、KATSUO、1234      NG:!$%&

 

  • re.IGNORECASE(re.I):大文字小文字を区別しない

例えば、「katsuo」「Katsuo」「KATSUO」のパターンがあった場合、このフラグを立てた場合は全てがマッチングします。

 

  • re.DOTALL(re.S):メタ文字の「.」が改行にもマッチ
  • re.VERBOSE(re.X):パターンの中にコメントを記述できる
  • re.MULTILINE(re.M):各行の先頭・末尾にマッチ
  • re.LOCALE(re.L):\w、\b、大文字・小文字を区別しないマッチングを、現在のロケールに依存
  • re.DEBUG:パターンに関するデバッグ情報を表示する

 

参考:

【python】re.match より re.search を使おう|Qiita @@halhorn

パターンから正規表現オブジェクトを作成する(Pattern)|Let's プログラミング

Pythonの正規表現で設定できるフラグの一覧|Let's プログラミング

正規表現|PythonエンジニアによるPython3学習サイト

Pythonの正規表現モジュールreの使い方(match、search、subなど)|note.nkmk.me

 

それでは今回はこちらで終了です。お付き合いいただきありがとうございました。

 


 

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  Microsoft Azure  Docker  Kubernetes