2

"spam bar ds<hai bye>sd baz eggs"のような文字列をリストにトークン化してみます。['spam', 'bar', 'ds<hai bye>sd', 'baz', 'eggs']つまり、str.split()内部の空白を保持します< ... >

私の解決策は、パターンで使用re.splitすることでした。(\S*<.*?>\S*)|\s+しかし、私は次のようになります。

>>> re.split('(\S*<.*?>\S*)|\s+', "spam bar ds<hai bye>sd baz eggs")
['spam', None, 'bar', None, '', 'ds<hai bye>sd', '', None, 'baz', None, 'eggs']

それらNoneと空の文字列がどこから来ているのかわからない。もちろん、リスト内包表記でそれらを除外することはできます[s for s in result if s]が、理由がわかる前にそれを行うのは快適ではありません。

それで、(1)なぜそれらNoneと空の文字列、(2)それはもっとうまくできるでしょうか?

4

3 に答える 3

3

空のNone文字列値は、パターンでキャプチャブラケットを使用したためです。したがって、分割には一致したテキストが含まれます。これについては、公式ドキュメントを参照してください。

パターンを次のように修正する場合r"((?:\S*<.*?>\S*)|\S+")(つまり、角かっこをエスケープして空白を非キャプチャにして非空白に修正する)、区切り文字を保持することによってのみ機能するはずです。区切り文字は、別の項目をスキップして除外する必要があります。私はあなたがこれでより良いと思います:

ITEM_RE = re.compile(r"(?:\S*<.*?>\S*)|\S+")
ITEM_RE.findall("spam bar ds<hai bye>sd baz eggs")

実際のリストが必要ない場合(つまり、一度に1つのアイテムしか調べない場合)、一度に1つのアイテムfinditer()しか生成されないため、より効率的です。これは、リスト全体を調べずに保釈する可能性が高い場合に特に当てはまります。

原則としてネガティブルックビハインドアサーションでも可能かもしれませんが、実際には十分に柔軟なものを作成することはできないと思います- r"(?<!<[^>]*)\s+"「ルックビハインドには固定幅パターンが必要です」というエラーが発生したので、それはいいえ、いいえ。ドキュメントはこれを裏付けています-ルックビハインドアサーション(ポジティブとネガティブの両方)はすべて固定幅である必要があります。

このアプローチの問題は、ネストされた山かっこを期待する場合に発生します。そうすると、期待したものが得られなくなります。たとえば、解析は1つのトークンとしてds<hai <bye> foo>sd生成されます。ds<hai <bye>これは、正規表現では対処できないクラスの問題だと思います。適切なパーサーに近いものが必要です。一度に文字を処理し、角かっこの入れ子レベルをカウントする純粋なPythonで作成するのは難しいことではありませんが、それはかなり遅くなります。入力に1レベルのネストしか表示されないことを確認できるかどうかによって異なります。

于 2013-03-07T20:19:51.587 に答える
1

私はこの正規表現を取得しました:

ss = "spam bar ds<hai bye>sd baz eggs ZQ<boo <abv> foo>WX  "

reg = re.compile('(?:'
                     '\S*?'
                     '<'
                     '[^<>]*?'
                     '(?:<[^<>]*>[^<>]*)*'
                     '[^<>]*?'
                     '>'
                       ')?'
                 '\S+')

print reg.findall(ss)

結果

['spam', 'bar', 'ds<hai bye>sd', 'baz', 'eggs',
 'ZQ<boo <abv> foo>WX']

編集1

Cartrooのコメントの後、より正確な新しい正規表現:

import re

pat = ('(?<!\S)'  # absence of non-whitespace before

       '(?:'
           '[^\s<>]+'

           '|'  # OR

           '(?:[^\s<>]*)'
           '(?:'
               '<'
               '[^<>]*?'
               '(?:<[^<>]*?>[^<>]*)*'
               '[^<>]*?'
               '>'
               ')'
           '(?:[^\s<>]*)'
       ')'

       '(?!\S)' # absence of non-whitespace after)
       )
reg = re.compile(pat)

ss = ("spam i>j bar ds<hai bye>sd baz eggs Z<boo <abv>"
      " foo>W ttt <two<;>*<:> three> ")
print '%s\n' % ss
print reg.findall(ss)

ss = "a<b<E1>c>d <b<E2>c>d <b<E3>c> a<<E4>c>d <<E5>>d 
   <<E6>> <<>>"
print '\n\n%s\n' % ss
print reg.findall(ss)

結果

spam i>j bar ds<hai bye>sd baz eggs Z<boo <abv> foo>W 
ttt <two<;>*<:> three> 

['spam', 'bar', 'ds<hai bye>sd', 'baz', 'eggs', 
 'Z<boo <abv> foo>W', 'ttt', '<two<;>*<:> three>']


a<b<E1>c>d <b<E2>c>d <b<E3>c> a<<E4>c>d <<E5>>d <<E6>> <<>>

['a<b<E1>c>d', '<b<E2>c>d', '<b<E3>c>', 'a<<E4>c>d', '<<E5>>d',
 '<<E6>>', '<<>>']

上記の文字列は整形式であり、結果は一貫しています。
整形式でないテキスト(角かっこに関して)では、望ましくない結果が生じる可能性があります。

ss = """A<B<C>D  
 E<F<G>H 
I<J>K> 
 L<<M>N
   O<P>>Q
 R<<S>    T<<>"""
print '\n\n%s\n' % ss
print reg.findall(ss)

結果

A<B<C>D  
 E<F<G>H 
I<J>K> 
 L<<M>N
   O<P>>Q
 R<<S>    T<<>

['E<F<G>H \nI<J>K>', 'L<<M>N\n   O<P>>Q']

これは、の終わりにある星のせいです'(?:<[^<>]*?>[^<>]*)*'。この動作は、星を削除することでオフにできます。この振る舞いは、Crtarooによって呼び出されるような「複雑な」テキストを分析するために正規表現を使用することを困難にするものです。

編集2

結果が望ましくないものであると言ったとき'E<F<G>H \nI<J>K>''L<<M>N\n O<P>>Q'それは、見つかった一致する部分が、私が作成した正規表現のパターン(どうしてそうなるのでしょうか?)を尊重していないという意味ではありませんでした。一致する部分は、確かに整形式です
。2つの部分は、2つのブラケットの間に<G>あり、2 つの部分は、2つのブラケットの間にあります。<J>< <G> <J> >
<M><P>< <M> <P> >

実際、見つかった各一致部分は1行だけに拡張する必要があることを意味する控えめな表現でした。しかし、控えめな表現が明示されるとすぐに、可能な解決策が浮かび上がります。
複数の行にまたがる一致する部分が望ましくない場合は、私が書いたものとは逆に、それらを一致させないように正規表現に指示するのは簡単です。\n正規表現のパターンのいくつかの場所に文字を追加するだけで十分です。

実際、これは、一致する部分が\n文字を通過してはならないことを意味し、この文字は一致する部分の区切り文字と見なすことができます。#したがって、たとえば次のコードでは 、同じ行に存在する一致する部分の間の区切り文字として、他の文字が必要になる場合があります。

正規表現は子供たちを料理したり学校から連れ出したりすることはできませんが、非常に強力です。不正な形式のテキストに対する正規表現の動作が問題であると言うのは短すぎます。正規表現ではなく、テキストの問題であると付け加える必要があります。正規表現は、指示されたとおりに実行します。つまり、正規表現に与えられたテキストをすべて食べます。そして、それは貪欲にそれを食べます。つまり、それについての適合性を確認せずに、それはそれからの意図された行動ではなく、それからそれが非食事的なテキストで供給された場合、それは責任を負いません。不正な形式のテキストに対する正規表現の動作が問題であると言うことは、誰かが子供にウイスキーや胡椒食品で栄養を与えられるように非難するかのように聞こえます。

正規表現に渡されるテキストが適切に形成されていることを確認するのは、コーダーの責任です。コーダーが検証スニペットをコードに入れて、プログラムが正しく実行されるようにエントリが整数であることを確認するのと同じ方法です。

この点は、マークアップされたテキストをXMLテキストとして解析しようとするときの正規表現の誤用とは異なります。不正な形式のマークアップされたテキストに正しく反応する正規表現を作成することは不可能であるため、正規表現はそのようなテキストを解析できません。それをやろうとしないのもコーダーの責任です。これは、このテキストが検証されている場合、マークアップされたテキストを分析するため
に正規表現を使用してはならないという意味ではありません。 とにかく、テキストがあまりにも奇形である場合、パーサーでさえデータをキャッチしません。

つまり、区別する必要があります。

  • 正規表現に渡されるテキストの性質(不正な形式/整形式)

  • 正規表現を使用するときに追求される目的の性質(解析/分析)

import re

ss = """
 A<:<11>:<12>:>
 fgh
 A<#:<33>:<34>:>
 A#<:<55>:<56>:>
 A<:<77>:<78> i<j>
 A<B<C>D #
 E<F<G>H #
 I<J>K> 
 L<<M>N 
 O<P>>Q  #
 R<<S>  T<<>"""
print '%s\n' % ss

pat = ('(?<!\S)'  # absence of non-whitespace before
           '(?:[^\s<>]*)'
           '(?:<'
               '[^<>]*?'
               '(?:<[^<>]*?>[^<>]*)*'
               '>)'
           '(?:[^\s<>]*)'
       '(?!\S)' # absence of non-whitespace after)
       )
reg = re.compile(pat)
print '------------------------------'
print '\n'.join(map(repr,reg.findall(ss)))


pat = ('(?<!\S)'  # absence of non-whitespace before
           '(?:[^\s<>]*)'
           '(?:<'
               '[^<>\n]*?'
               '(?:<[^<>\n]*?>[^<>\n]*)*'
               '>)'
           '(?:[^\s<>]*)'
       '(?!\S)' # absence of non-whitespace after)
       )
reg = re.compile(pat)
print '\n----------- with \\n -------------'
print '\n'.join(map(repr,reg.findall(ss)))


pat = ('(?<!\S)'  # absence of non-whitespace before
           '(?:[^\s<>]*)'
           '(?:<'
               '[^<>#]*?'
               '(?:<[^<>#]*?>[^<>#]*)*'
               '>)'
           '(?:[^\s<>]*)'
       '(?!\S)' # absence of non-whitespace after)
       )
reg = re.compile(pat)
print '\n------------- with # -----------'
print '\n'.join(map(repr,reg.findall(ss)))


pat = ('(?<!\S)'  # absence of non-whitespace before
           '(?:[^\s<>#]*)'
           '(?:<'
               '[^<>#]*?'
               '(?:<[^<>#]*?>[^<>#]*)*'
               '>)'
           '(?:[^\s<>]*)'
       '(?!\S)' # absence of non-whitespace after)
       )
reg = re.compile(pat)
print '\n------ with ^# everywhere -------'
print '\n'.join(map(repr,reg.findall(ss)))

結果

 A<:<11>:<12>:>
 fgh
 A<#:<33>:<34>:>
 A#<:<55>:<56>:>
 A<:<77>:<78> i<j>
 A<B<C>D #
 E<F<G>H #
 I<J>K> 
 L<<M>N 
 O<P>>Q  #
 R<<S>  T<<>

------------------------------
'A<:<11>:<12>:>'
'A<#:<33>:<34>:>'
'A#<:<55>:<56>:>'
'i<j>'
'E<F<G>H #\n I<J>K>'
'L<<M>N \n O<P>>Q'

----------- with \n -------------
'A<:<11>:<12>:>'
'A<#:<33>:<34>:>'
'A#<:<55>:<56>:>'
'i<j>'

------------- with # -----------
'A<:<11>:<12>:>'
'A#<:<55>:<56>:>'
'i<j>'
'L<<M>N \n O<P>>Q'

------ with ^# everywhere -------
'A<:<11>:<12>:>'
'i<j>'
'L<<M>N \n O<P>>Q'
于 2013-03-07T22:12:26.530 に答える
0

値は、ドキュメントのこの行に基づくパターンNoneにsが存在するためだと思います。()

キャプチャ括弧がパターンで使用されている場合、パターン内のすべてのグループのテキストも結果のリストの一部として返されます

入力で正規表現テスターを使用すると、解析を視覚化するのにも役立つ場合があります:http: //regexpal.com/?flags=g®ex=%28\S*%3C.*%3F%3E\S*%29|\s%2B&input = spam%20bar%20ds%3Chai%20bye%3Esd%20baz%20eggs

于 2013-03-07T20:24:29.127 に答える