2011-08-11 12 views
16

私は、いくつかの日付の値を持つ文字列を持っており、それらをすべて解析したいと思います。文字列は自然言語なので、私が今までに見つけた最良のものはdateutilです。長い文字列からすべての日付を解析する方法についてPython(または他の言語)のテキストブロックから複数の日付を解析する方法

>>> s = "I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928" 
>>> parse(s, fuzzy=True) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/pymodules/python2.7/dateutil/parser.py", line 697, in parse 
    return DEFAULTPARSER.parse(timestr, **kwargs) 
    File "/usr/lib/pymodules/python2.7/dateutil/parser.py", line 303, in parse 
    raise ValueError, "unknown string format" 
ValueError: unknown string format 

任意の考え:文字列がその中に複数の日付の値を持つ場合

残念ながら、dateutilは、エラーがスローされますか?理想的には、リストが作成されますが、必要に応じてそれを処理できます。

私はPythonを使用していますが、この時点で他の言語はおそらく仕事を終えてもOKです。

PS - 入力ファイルを途中で再帰的に分割して試してみると、うまくいくまで再試行することができますが、ハックの地獄です。

+0

の最大値を収集することですあなたが解析したい日付であることを「イースターに」検討していますか? – MattH

+0

Nah。それが働いているかどうかを調べるためにテストしていましたが、いずれにしてもあまり気にしません。 – mlissner

+0

DateUtil 1.5では、もちろん動作しますが、私の悪いです。しかし、私はまだMattH Shawn Chinよりもクリーナー/スピードの速いアプローチを手に入れたいと思っています。 – Dieter

答えて

15

これを見ると、最も不快なやり方は、dateutil parserを変更してファジー多重オプションを持つことです。

parser._parseは文字列を取り、_timelexでトークン化し、次にトークンをparserinfoで定義されたデータと比較します。

Hereの場合、parserinfoのトークンと一致しない場合、fuzzyがTrueでなければ解析が失敗します。

処理された時間トークンがない状態で不一致を許可することをお勧めします。不一致の場合は、その時点で解析されたデータを処理し、再び時間トークンを探し始めます。

多大な労力を要しません。


更新

あなたのパッチがで...

ロールを取得するために待っている間これは、少しハックされ、ライブラリに未公開の機能を使用しますが、doesnのライブラリを変更する必要があり、試行錯誤が必要ではありません。浮動小数点に変換できる唯一のトークンを持っている場合は、誤検出する可能性があります。結果をさらに絞り込む必要があるかもしれません。

from dateutil.parser import _timelex, parser 

a = "I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928" 

p = parser() 
info = p.info 

def timetoken(token): 
    try: 
    float(token) 
    return True 
    except ValueError: 
    pass 
    return any(f(token) for f in (info.jump,info.weekday,info.month,info.hms,info.ampm,info.pertain,info.utczone,info.tzoffset)) 

def timesplit(input_string): 
    batch = [] 
    for token in _timelex(input_string): 
    if timetoken(token): 
     if info.jump(token): 
     continue 
     batch.append(token) 
    else: 
     if batch: 
     yield " ".join(batch) 
     batch = [] 
    if batch: 
    yield " ".join(batch) 

for item in timesplit(a): 
    print "Found:", item 
    print "Parsed:", p.parse(item) 

収量:ディーター

Dateutil 2.1が表示されますがのpython3との互換性のために書かれたとsixと呼ばれる "互換性" ライブラリを使用するための

Found: 2011 04 23 
Parsed: 2011-04-23 00:00:00 
Found: 29 July 1928 
Parsed: 1928-07-29 00:00:00

を更新。何かがそれで正しくなく、strのオブジェクトをテキストとして扱っていません。

このソリューションはdateutil 2で動作します。1あなたはユニコードとして、あるいはファイルのようなオブジェクトとして文字列を渡す場合:

from cStringIO import StringIO 
for item in timesplit(StringIO(a)): 
    print "Found:", item 
    print "Parsed:", p.parse(StringIO(item)) 

あなたは、parserinfo上のオプションを設定parserinfoをインスタンス化し、パーサオブジェクトにそれを渡したい場合。例:

from dateutil.parser import _timelex, parser, parserinfo 
info = parserinfo(dayfirst=True) 
p = parser(info) 
+0

おそらく最も効率的なソリューションです。 +1。もちろん、ライブラリ自体を変更すると、変更が公式ソースに吸収されない限り、展開/保守が少し難しくなります。 –

+0

これはすばらしい答えです。これまでに私が得た最高のものです。それをテストして、それがどのように進むのかを教えてくれます。ありがとう! – mlissner

+0

これは非常にうまくいく。それは私が捕捉する必要があるTypeErrorsとValueErrorsと多くの誤検出を持っています。エラーは簡単に捕捉できます。私は今年から何かを壊して誤検出を排除しています(私のコーパスには古い日付しかありません)。再度、感謝します。 – mlissner

5

私はオフラインで、昨日ここに投稿した回答に悩まされました。はい、それは仕事をしましたが、それは不必要に複雑で非常に非効率でした。

これは、はるかに良い仕事をする必要があります封筒の後編です!

import itertools 
from dateutil import parser 

jumpwords = set(parser.parserinfo.JUMP) 
keywords = set(kw.lower() for kw in itertools.chain(
    parser.parserinfo.UTCZONE, 
    parser.parserinfo.PERTAIN, 
    (x for s in parser.parserinfo.WEEKDAYS for x in s), 
    (x for s in parser.parserinfo.MONTHS for x in s), 
    (x for s in parser.parserinfo.HMS for x in s), 
    (x for s in parser.parserinfo.AMPM for x in s), 
)) 

def parse_multiple(s): 
    def is_valid_kw(s): 
     try: # is it a number? 
      float(s) 
      return True 
     except ValueError: 
      return s.lower() in keywords 

    def _split(s): 
     kw_found = False 
     tokens = parser._timelex.split(s) 
     for i in xrange(len(tokens)): 
      if tokens[i] in jumpwords: 
       continue 
      if not kw_found and is_valid_kw(tokens[i]): 
       kw_found = True 
       start = i 
      elif kw_found and not is_valid_kw(tokens[i]): 
       kw_found = False 
       yield "".join(tokens[start:i]) 
     # handle date at end of input str 
     if kw_found: 
      yield "".join(tokens[start:]) 

    return [parser.parse(x) for x in _split(s)] 

使用例:

>>> parse_multiple("I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928") 
[datetime.datetime(2011, 4, 23, 0, 0), datetime.datetime(1928, 7, 29, 0, 0)] 

それはおそらく空/未知の文字列を扱うときの挙動がdateutil.parser.parseから少し外れていることは注目に値します。 Dateutilは現在の日を返しますが、parse_multipleはIMHOが期待する空リストを返します。

>>> from dateutil import parser 
>>> parser.parse("") 
datetime.datetime(2011, 8, 12, 0, 0) 
>>> parse_multiple("") 
[] 

P.S.ちょうど非常に似た何かをするちょうどMattH's updated answerを見つけた。

+0

これはMattHの提案よりも最初は信頼できるようでしたが、パフォーマンスは大きなテストでは驚くことではありませんでした。しかし、助けてくれてありがとう! – mlissner

+0

@ mlissnerよろしくお願いします。解決するのは楽しい問題でした。私は昨晩それを考えていたので、もっと良い解決策だと思いました。更新された回答をご覧ください。 –

0

"単語"を配列に入れると、そのトリックを行うべきだと思います。これで、日付か否かを確認し、変数に入れることができます。

日付を取得したら、datetime libraryライブラリを使用する必要があります。

0

日付が表示される可能性のあるすべてのフォームをカバーする正規表現パターンを作成して正規表現を起動してテキストを検索するのはなぜですか?私は、文字列で日付を表現するための数十ものマナーがないと推測します。

唯一の問題はあなたのサンプル文字列で日付の表情

関連する問題