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

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

新規登録して質問してみよう
ただいま回答率
85.49%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

正規表現

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

Q&A

解決済

3回答

1581閲覧

reモジュールでの複数の~~コンパイルフラグ~~ビットフラグの記法と、~~論理和~~ビット和

N-B-I

総合スコア12

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

正規表現

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

0グッド

0クリップ

投稿2020/02/25 11:11

編集2020/05/20 15:30

前提

「退屈なことはPythonにやらせよう」7章14節
re.IGNORECASEとre.DOTALLとre.VERBOSEを組み合わせる より

re.compile()は、第2引数に1つの値しかとりません。
このような場合は、re.IGNORECASEやre.DOTALLやre.VERBOSEを、縦線記号(|)で結合します。
この文脈での|は「論理和演算子」として働きます。
したがって、大文字と小文字を区別せず、ドット文字を改行にマッチさせたい場合には、re.compile()を次のように呼び出します。

Python3

1some_regex_value = re.compile(正規表現,re.IGNORECASE | re.DOTALL)

3つのオプションを全部指定するには、次のように書きます。

Python3

1some_regex_value = re.compile(正規表現,re.IGNORECASE | re.DOTALL | re.VERBOSE)

この記法は、初期のPythonから引き継いだ若干時代遅れのものです。論理和については本書の範囲を超えるので、詳細はhttps://wiki.python.org/moin/BitwiseOperators/(英文)を参照してください。

環境

Windows10
Python 3.7.4 64-bit

質問

1.
なぜ&でなくて|なのでしょうか。
どちらでもいいのか、あるいは|でないといけない理由があるのでしょうか?
画像2枚目のように図にすると|の場合黒斜線、&の場合赤斜線の部分になるはずで、
極端な話、Regexオブジェクトを生成する度に2/3の確率でre.Iかre.Sいずれかが無効になってmo == Noneになったりしないのはなぜですか?
|と&
集合

2.

この記法は、初期のPythonから引き継いだ若干時代遅れのものです。

について、では新しい記法ではどのように書くのが好ましいのでしょうか?



解決後記、皆様御回答ありがとうございます

まとめ

  • re.IGNORECASE、re.DOTALL、re.VERBOSEなどはビットフラグ。
  • |はビット和演算子。
  • ビット演算子は2進数表現の各桁同士に対してブール演算を行う。

2進数を数値ではなく大量のON/OFFスイッチが並んでいると捉えると、
何故なんのためにビット演算をするのか突然理解できました。
つまり大量の0が並ぶ中唯一何桁目が1なのかでフラグが立つ/立たないを区別しているだけの記号なのであって、
RegexFlag.VERBOSEが64であるとかRegexFlag.IGNORECASEが2であるとか、
10進数に直すと結果的にそうなっているだけで、数値の大小自体に優劣等の特別な意味合いがあるわけではないのですね。

以下に参考にしたソースを付記します

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

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

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

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

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

guest

回答3

0

ベストアンサー

この文脈での|は「論理和演算子」として働きます。

この記述は間違いです。論理和演算子は普通はor演算子のことです。
|は、普通はビット和演算子と呼びます。整数の2進数表現をビット単位で和を求めたものです。
質問にお書きのような意味はありません。

今回の例では、ビット単位で意味を与えられた引数を、重ね合わせることになります。
互いに独立したオン・オフのオプションをどれでも組み合わせて複数指定できる場合に使われたりします。
ビット単位の和で、足し算での繰上りが無いので、別のビット(他のオプション)に影響することがありません。

投稿2020/02/25 15:39

otn

総合スコア84487

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

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

N-B-I

2020/02/26 08:32

> 別のビット(たお)に影響することがありません。 すみません、たお ってなんでしょうか?
otn

2020/02/27 09:02 編集

タイプミスしたか??と思って、編集画面に入ると、ちゃんと > 別のビット(他のオプション)に影響することがありません。 と書いてありました。このサイトのバグのようです。 表示が途中で切れてしまうバグはちょくちょく遇いますが、このパターンは初めて。
hayataka2049

2020/02/27 09:32

そのパターンも編集画面で一文字変えて上書きとかで直せはします。
N-B-I

2020/02/28 05:01 編集

> サイトのバグ 承知しました。 後から確認できるようにこちらでどう見えているかの画像を残します https://imgur.com/TvrSUhc
guest

0

まず前提として、re.IGNORECASE,re.DOTALL,re.VERBOSEなどはすべて整数型です。

(LouiS0616さんの回答にもある通り、Python 3.6以降では列挙型になっているので通常のintとはちょっと異なる特殊な整数なのですが、整数として扱えることには代わりありません)

python

1>>> import re 2>>> (re.IGNORECASE, re.DOTALL, re.VERBOSE) 3(<RegexFlag.IGNORECASE: 2>, <RegexFlag.DOTALL: 16>, <RegexFlag.VERBOSE: 64>)

カンマで区切られた要素の>の左にある数字に注目してください。すべて2^nの数字になっていることがおわかりいただけるかと思います。

それぞれ2進数で書くと(8bitで表すとして)

  • 0000 0010 2
  • 0001 0000 16
  • 0100 0000 64

になりますね。

さて、整数のビット演算子を用いるとビットの各桁同士に対してブール演算を行いますから、|で3つを結ぶと0101 0010(82)が得られます。3つのオペランドのうちどれか1つでその桁のビットが立っていたら、最終結果のビットは立ちます。

どこのビットが立っているのかを判断することでどのオプションが指定されたのか判断できるので、これは理にかなっています。

&は逆に、オペランドすべてでその桁のビットが立っていないとその桁の値は0になるので、とても都合が悪いことがわかります。


質問文の画像はコンパイル済み正規表現オブジェクトを生成したあとに

python

1mo = regex.search('''H 2ge''')

を実行していないため、最初にやった結果が入ったままになっています。これをもう一度実行してmoを作り直せば違った結果になるでしょう。

投稿2020/02/25 11:39

編集2020/02/25 11:54
hayataka2049

総合スコア30933

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

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

N-B-I

2020/02/27 07:04

> これをもう一度実行して`mo`を作り直せば違った結果になるでしょう。 ご指摘ありがとうございます、値渡しと参照渡しについてもう一度学びなおしてきます。
hayataka2049

2020/02/27 10:58

値渡しと参照渡しには関係ありません。
N-B-I

2020/02/28 05:03

現状どこを理解できていないのかもはっきりしないので、 調べてわからなかった場合は別で質問を建てようと思います、どうもすみません。
guest

0

いわゆるビットフラグです。
試しに各要素の値を覗いてみると面白いですよ。

Python

1>>> import re 2>>> 3>>> flags = [re.ASCII, re.DEBUG, re.IGNORECASE, re.LOCALE, re.MULTILINE, re.DOTALL, re.VERBOSE] 4>>> 5>>> for flag in flags: 6... print(type(flag), flag) 7... value = flag.value # Enum の値 8... print(type(value), f'{value:b}') # valueを2進数で表示 9... 10<enum 'RegexFlag'> RegexFlag.ASCII 11<class 'int'> 100000000 12<enum 'RegexFlag'> RegexFlag.DEBUG 13<class 'int'> 10000000 14<enum 'RegexFlag'> RegexFlag.IGNORECASE 15<class 'int'> 10 16<enum 'RegexFlag'> RegexFlag.LOCALE 17<class 'int'> 100 18<enum 'RegexFlag'> RegexFlag.MULTILINE 19<class 'int'> 1000 20<enum 'RegexFlag'> RegexFlag.DOTALL 21<class 'int'> 10000 22<enum 'RegexFlag'> RegexFlag.VERBOSE 23<class 'int'> 1000000

このとき論理和ビット和は数値の加算と同じです。
各要素の値は2進表記したとき桁が被らないよう選ばれているので、一意に分解できます。

Python

1>>> f'{re.ASCII + re.DOTALL:b}' 2'100010000' 3>>> f'{re.ASCII | re.DOTALL:b}' 4'100010000'

なぜ&でなくて|なのでしょうか。

前述したとおり、都合が良いからです。
またビットフラグは長く用いられているので、別の記号を用いると混乱を招きます。

新しい記法ではどのように書くのが好ましいのでしょうか?

ビットフラグではなく、キーワード引数として提供されるのを想定しているのではないでしょうか。

Python

1re.compile(r'h..e', ignore_case=True, dot_all=True) # 註:実際にはこのように書けません。

あるいはこのように正規表現文字列の中にフラグを埋め込むことができます。

Python

1re.compile(r'h..e', re.IGNORECASE | re.DOTALL) # これと 2re.compile(r'(?is)h..e') # これは同じ

ただこれはこれで読みづらく浸透していない(ような気がする)ので、
ビットフラグを用いた、つまり古い書き方を引き続き用いても良いように思います。

投稿2020/02/25 11:20

編集2020/02/25 23:29
LouiS0616

総合スコア35660

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

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

LouiS0616

2020/02/25 11:46

CPython3.6以降、フラグの型がintからRegexFlagに変わっているようです。 What's new には記載されていませんが、おそらくIntFlagの導入に伴う変更でしょう。
LouiS0616

2020/02/25 23:30

otnさんの回答を見て間違いに気付き、一部修正しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問