質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

2回答

1841閲覧

正規表現 (?=.*pattern)と(?=.*?pattern)の実行結果は同じ?

chida3

総合スコア15

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

0グッド

0クリップ

投稿2018/05/13 07:47

編集2018/05/13 09:57

パスワードのバリデーションのようなものを正規表現を使って書いていたのですが、
なぜ下記のような結果になるのかよくわからないです。

python

1import re 2 3argStr = '123aBcde' 4 5print('*1*') 6print(re.compile(r'.*(?=.*?[a-z])').search(argStr).group()) 7print(re.compile(r'.*(?=.*[a-z])').search(argStr).group()) 8#文字列を切り出すため、肯定先読みの表現の前に「.*」を入れています 9 10print('*2*') 11print(re.compile(r'(.*?[a-z])').search(argStr).group()) 12print(re.compile(r'(.*[a-z])').search(argStr).group()) 13

期待値
*1*
123a 123(※訂正しました)
123aBcde 123aBcd(※訂正しました)
*2*
123a
123aBcde

実行
*1*
123aBcde 123aBcd(※訂正しました)   ・・・(*)
123aBcde 123aBcd(※訂正しました)
*2*
123a
123aBcde

非貪欲マッチを指定しているので、(*)でも「123a」となると思っていたのですが、
そのようになりませんでした。

(?=.pattern)と(?=.?pattern)の実行結果はなぜ同じになってしまうのでしょうか。
ご教示賜りたいです。

**************
すみません、期待値および実行結果に一部タイプミスがあったため、訂正いたしました。

**追記**********
本来やろうとしていたことは、下記のようなバリデーションチェックです。
・英小文字、英大文字、数字がそれぞれ1文字以上あること
・全体で8文字以上であること

これ自体は、「^(?=.[a-z])(?=.[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$」とすればよいことが確認できました。

ただ、上記の書き方にたどりつくまでにいくつか調べていたところ、
今回質問したように「.?*」という書き方をしているものがありました。
https://qiita.com/momotaro98/items/460c6cac14473765ec14

passw0rd という文字列であれば、pとして行頭が^(?=.?[a-z])にマッチしていることになり、また、passw0として行頭が^(?=.?\d)にマッチしていることになります。

上記のとおりだと、(*)の実行結果は「123」になるのでは?と思ったのが、今回の質問のきっかけです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

a_saitoh

2018/05/13 09:36

本当に先読みアサーションをしたいのかどうかと、どういう意味の正規表現を書いたつもりなのか、とを追記した方が良いと思います。「先頭から、最初に英子文字が出てくる処の前の文字まで」だったら[^a-z]*で済みますし。
guest

回答2

0

まず、(?=...)グループを形成しません ので、(?=...)とマッチしたものはグループとして取れません。そして、.group()と引数なしで呼び出すと、ヒットした全体を返すようになっています。

つまり、1の方で取ってきているのは、.*にヒットしたほうです。

正規表現に関する、Python3のリファレンス

投稿2018/05/13 08:13

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

chida3

2018/05/13 10:23

実行結果の当初の記載が間違っていたため、改めました。 「(?=...)はグループを形成しません」とは(?=...)で得られるのは「位置」だからという意味でしょうか。
maisumakun

2018/05/13 11:10

いちばん端的にいえば、そう決まっているからです(「拡張記法(注:`(?...)`のブロック)は普通新しいグループを作成しません」と、上のリファレンスにもあります)。
chida3

2018/05/13 11:17

該当箇所見落としていました。ありがとうございます。
guest

0

ベストアンサー

実行

*1* 123aBcde    ・・・(*) 123aBcde *2* 123a 123aBcde

Pythonは知りませんが、一般的な正規表現エンジンではあり得ない結果だったので、ideoneで検証したら期待通りの結果でした。

*1* 123aBcd 123aBcd *2* 123a 123aBcde

(?=.pattern)と(?=.?pattern)の実行結果はなぜ同じになってしまうのでしょうか。

.*? の最短一致は空文字です。
そして、前方に最長一致があれば、そちらが優先されます。

Python

1print(re.compile(r'.*(?=.*?[a-z])').search(argStr).group())

.*? の最短一致は空文字なので、この正規表現は '.*(?=[a-z])' と同義です。
.* は最長一致なので、貪欲に消費します。

123aBcd

Re: chida3 さん

投稿2018/05/13 08:09

think49

総合スコア18162

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

chida3

2018/05/13 10:18

コメントありがとうございます。 ちょっとみにくいですが、検査対象の文字列の文字の前後の位置を[]で示したとして、  [0]1[1]2[2]3[4]a[5]B[6]c[7]d[8]e[9] ■.*(?=.*[a-z])の場合  (?=.*[a-z])の結果は[8]となる。冒頭の.*と合わせて、「123aBcd」。 ■.*(?=.*?[a-z])の場合  .*(?=[a-z])と同じ。  .*(?=[a-z])の結果は[4]or[5]or[6]or[7]or[8]のうちから最終的に[8]が該当する。  冒頭の.*と合わせて、「123aBcd」。 書いていただいた内容を自分なりにかみ砕くとこうなったのですが、あっていますでしょうか?
think49

2018/05/13 10:53 編集

.*(?=.*[a-z]) ---> (?=.*[a-z])の結果は "e" .*(?=.*?[a-z]) ---> (?=.*?[a-z])の結果は "e" .*(?=.*?[a-z]) ---> 消費値は "[0]1[1]2[2]3[4]a[5]B[6]c[7]d[8]"
think49

2018/05/13 10:54

(?=pattern) は消費しません。.* は消費します。
chida3

2018/05/13 11:27

ちょっと理解できた気がします。 (?=.*[a-z]) と(?=.*?[a-z])自体の結果は同じで「e」、その直前までの分について任意の文字列(.*)を消費するから、「123aBcd」となるということですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問