2012-01-18 7 views
4

大文字の4番目の出現からテキストの行を解析するにはどうすればよいですか?たとえば、与えられた行は:私は、正規表現より良い方法は、おそらくそこにあると確信しているPythonの第4大文字を解析しますか?

`ZsdalkjgalsdkjTlaksdjfgasdkgj` 
`PlsdakjfsldgjQ` 

を、私は非欲張りを行うことを試みた。私は希望

adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj 
oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ 

をキャプチャします一致;このような何か:

match = re.search(r'[A-Z].*?$', line).group() 
+0

私はこの問題について考えており、より簡単な解決策を考え出しました。私の更新された答えを見てください。 – NPE

答えて

9

は、私は2つのアプローチを提示します。

アプローチ1:すべてのアウトの正規表現

In [1]: import re 

In [2]: s = 'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj' 

In [3]: re.match(r'(?:.*?[A-Z]){3}.*?([A-Z].*)', s).group(1) 
Out[3]: 'ZsdalkjgalsdkjTlaksdjfgasdkgj' 

.*?[A-Z]は、まで文字を消費し、そして、最初の大文字を含みます。

(?: ... ){3}は、キャプチャグループを作成せずに上記を3回繰り返します。

次の.*?は、第4大文字の前の残りの文字と一致します。

最後に、([A-Z].*)は、4番目の大文字とそれに続くすべてをキャプチャグループにキャプチャします。

アプローチ2:単純な正規表現

In [1]: import re 

In [2]: s = 'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj' 

In [3]: ''.join(re.findall(r'[A-Z][^A-Z]*', s)[3:]) 
Out[3]: 'ZsdalkjgalsdkjTlaksdjfgasdkgj' 

これが直接問題を攻撃する、と私は読みやすいと思います。とにかく、正規表現を使用していない

+0

ありがとう@aix。私はあなたの2番目の解決策がこれまでのところ最も簡潔で可読な解決であることに同意します。ほとんどの人は、スライスと組み合わせた最小限の正規表現の組み合わせが最良のアプローチだと同意したようです。 – drbunsen

+0

それは私の答えよりもちょっとかわいいかもしれません。 – kindall

0

それはきれいなアプローチではないですが、:

re.match(r'([^A-Z]*[A-Z]){3}[^A-Z]*([A-Z].*)', line).group(2) 
0
import re 
strings = [ 
    'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj', 
    'oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ', 
] 

for s in strings: 
    m = re.match('[a-z]*[A-Z][a-z]*[A-Z][a-z]*[A-Z][a-z]*([A-Z].+)', s) 
    if m: 
     print m.group(1) 
4

はあまりにも冗長であることを見ます - bytcodelevelで、それは非常に単純なアルゴリズムを実行するため、軽量であるにもかかわらず。

def find_capital(string, n=4): 
    count = 0 
    for index, letter in enumerate(string): 
     # The boolean value counts as 0 for False or 1 for True 
     count += letter.isupper() 
     if count == n: 
      return string[index:] 
    return "" 
+1

+1。 'timeit'を使う。 'のS = Sでの=「adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj」が正規表現方式に3.2S;私はこの方法が正規表現方式よりも約50%遅いことを見出した(一度上記の方法を定義するの百万回のセットアップが4.7sの列挙方法をとるre.match (3)。*?([AZ]。*) '、s).group(1) ');はるかに読みやすく、維持しやすく、透明です。 –

+0

実際には約100%遅くなります。私は実際に私が似ていたがわずかに異なる機能を比較した。回答は私の答えを見てください。 –

+0

私の方法のタイミングをとってくれてありがとう。 – jsbueno

1

:退屈なのに、確かにダウン可読性の手の中に任意の適切な正規表現を打つ、彼らはネイティブコードで実装されているので

それは、より速くそのregexpsareであってもよいが、「それを行うには1つの明白な方法」私はこれがあなたのために働く、そして将来的に拡張することが非常に簡単になることを信じて:

check = 'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj' 
print re.match('([^A-Z]*[A-Z]){3}[^A-Z]*([A-Z].*)', check).group(2) 

正規表現([^A-Z]*[A-Z]){3}の最初の部分は、実際の鍵であり、これは最初の3つの大文字を見つけ、一緒にそれらを保存しますそれらの間の文字をグループ1では、3番目の大文字の後に任意の数の大文字でない文字をスキップし、最後に残りの文字列を取り込みます。

0

ほとんどの場合、解析には正規表現が必要です。しかし、正規表現は、それ自体がであり、パーサーは作成されません。最も単純な意味では、パーサーはからなる:

text input stream -> tokenizer 

通常、それは追加のステップを有する:

text input stream -> tokenizer -> parser 

ようトークナイザハンドルは、入力ストリームを開き、適切な方法でテキストを収集しますプログラマはそれについて考える必要はありません。利用可能なマッチが1つしかない限り、テキスト要素を消費します。次に、この "トークン"に関連付けられたコードを実行します。あなたはトークナイザをお持ちでない場合は、(擬似コードで)それを自分をロールする必要があります:あなたがそれらのすべての時間を構築しない限り、

while stuffInStream: 
    currChars + getNextCharFromString 
    if regex('firstCase'): 
     do stuff 
    elif regex('other stuff'): 
     do more stuff 

このループコードは、落とし穴がいっぱいです。また、一連のルールからコンピュータに生産させるのも簡単です。それがLex/flexの仕組みです。トークンに関連付けられたルールを、パーサーとしてトークンをyacc/bisonに渡すようにすると、構造が追加されます。

レクサーはちょうどstate machineであることに注意してください。状態から状態に移行するときは、何かを実行できます。私は入力ストリームから文字を取り除き、ファイルを開いたり、テキストを印刷したり、電子メールを送信したりするレクサーを作成しました。

したがって、4番目の大文字の後にテキストを収集するだけであれば、正規表現は適切であるだけでなく、正しい解決策です。 BUTテキスト入力の解析をにしたい場合は、何を行うべきかと入力量が不明なため、レクサー/パーサーが必要です。あなたはPythonを使っているので、私はPLYを提案します。

0
caps = set("ABCDEFGHIJKLMNOPQRSTUVWXYZ") 
temp = '' 
for char in inputStr: 
if char in caps: 
    temp += char 
    if len(temp) == 4: 
     print temp[-1] # this is the answer that you are looking for 
     break 

また、あなたは大文字ではないものを取り除くためにre.subを使用して、素敵な、1行のソリューションは、可能性が

2

を残っているものの第四文字得ることができます:

>>> s1 = 'adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj' 
>>> s2 = 'oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ' 
>>> s1[list(re.finditer('[A-Z]', s1))[3].start():] 
'ZsdalkjgalsdkjTlaksdjfgasdkgj' 
>>> s2[list(re.finditer('[A-Z]', s2))[3].start():] 
'PlsdakjfsldgjQ' 

これはなぜ(1行で)動作しますか?文字列内のすべての大文字用

  • キーワード:re.finditer('[A-Z]', s1)
  • が見つかっ第四大文字を取得します:[3]
  • は、第四大文字からの位置を返します:スライス表記を使用.start()
  • 、我々が得ます文字列から必要な部分s1[position:]
1

さまざまな方法をテストします。私はオリジナルはstring_after_Nth_upperと書いていませんでした。 jsbuenoの方法も同様であることが分かりました。ただし、すべての文字(小文字でも)の加算/カウントの比較を行うことを除いて、彼の方法はやや遅くなります。

s='adsasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj' 
import re 
def string_after_Nth_upper(your_str, N=4): 
    upper_count = 0 
    for i, c in enumerate(your_str): 
     if c.isupper(): 
      upper_count += 1 
      if upper_count == N: 
       return your_str[i:] 
    return "" 

def find_capital(string, n=4): 
    count = 0 
    for index, letter in enumerate(string): 
     # The boolean value counts as 0 for False or 1 for True 
     count += letter.isupper() 
     if count == n: 
      return string[index:] 
    return "" 

def regex1(s): 
    return re.match(r'(?:.*?[A-Z]){3}.*?([A-Z].*)', s).group(1) 
def regex2(s): 
    return re.match(r'([^A-Z]*[A-Z]){3}[^A-Z]*([A-Z].*)', s).group(2) 
def regex3(s): 
    return s[list(re.finditer('[A-Z]', s))[3].start():] 
if __name__ == '__main__': 
    from timeit import Timer 
    t_simple = Timer("string_after_Nth_upper(s)", "from __main__ import s, string_after_Nth_upper") 
    print 'simple:', t_simple.timeit() 
    t_jsbueno = Timer("find_capital(s)", "from __main__ import s, find_capital") 
    print 'jsbueno:', t_jsbueno.timeit() 
    t_regex1 = Timer("regex1(s)", "from __main__ import s, regex1; import re") 
    print "Regex1:",t_regex1.timeit() 
    t_regex2 = Timer("regex2(s)", "from __main__ import s, regex2; import re") 
    print "Regex2:", t_regex2.timeit() 

    t_regex3 = Timer("regex3(s)", "from __main__ import s, regex3; import re") 
    print "Regex3:", t_regex3.timeit() 

結果:時間

Simple: 4.80558681488 
jsbueno: 5.92122507095 
Regex1: 3.21153497696 
Regex2: 2.80767202377 
Regex3: 6.64155721664 

のでregex2勝利。

0

もう1つのバージョン...それほど美しくはないが、仕事は終わる。

def stringafter4thupper(s):  
    i,r = 0,'' 
    for c in s: 
     if c.isupper() and i < 4: 
      i+=1 
     if i==4: 
      r+=c 
    return r 

例:

stringafter4thupper('adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj') 
stringafter4thupper('oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ') 
stringafter4thupper('') 
stringafter4thupper('abcdef') 
stringafter4thupper('ABCDEFGH') 

それぞれの結果:この単純に結果のリストをスライスした後、文字列を分割するために正規表現を使用することによって対処することが分かっ

'ZsdalkjgalsdkjTlaksdjfgasdkgj' 
'PlsdakjfsldgjQ' 
'' 
'' 
'DEFGH' 
3

import re 

text = ["adsgasdlkgasYasdgjaUUalsdkjgaZsdalkjgalsdkjTlaksdjfgasdkgj", 
     "oiwuewHsajlkjfasNasldjgalskjgasdIasdllksjdgaPlsdakjfsldgjQ"] 

for t in text: 
    print "".join(re.split("([A-Z])", t, maxsplit=4)[7:]) 

C十分に大文字がない場合は空文字列を返します。

+0

非常にいいです、私はこれが最良のソリューションだと思います。私は大文字で分割するために 're(r)(?<=。)([A-Z]) '、r' \ 1 '、line)'を使用しましたが、 – drbunsen

関連する問題