2011-08-11 16 views
16

मेरे पास एक स्ट्रिंग है जिसमें इसमें कई दिनांक मान हैं, और मैं उन्हें सभी को पार्स करना चाहता हूं। स्ट्रिंग प्राकृतिक भाषा है, इसलिए अब तक की सबसे अच्छी चीज dateutil है।पायथन (या दूसरी भाषा) में टेक्स्ट के ब्लॉक से कई तिथियों का विश्लेषण कैसे करें

>>> 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 एक त्रुटि फेंकता है? आदर्श रूप में, एक सूची बनाई जाएगी, लेकिन अगर मुझे आवश्यकता है तो मैं इसे स्वयं संभाल सकता हूं।

मैं पायथन का उपयोग कर रहा हूं, लेकिन इस बिंदु पर, यदि वे काम पूरा करते हैं तो अन्य भाषाएं शायद ठीक हैं।

पीएस - मुझे लगता है कि मैं बाद में इनपुट फ़ाइल को विभाजित कर सकता हूं और कोशिश कर सकता हूं, जब तक यह काम नहीं करता तब तक पुनः प्रयास करें, लेकिन यह एक हैक का नरक है।

+0

अपने नमूना स्ट्रिंग में आप विचार कर रहे हैं की अधिकतम इकट्ठा करने के लिए हो रहा है? – MattH

+0

नहीं। यह देखने के लिए परीक्षण किया गया था कि यह काम करता है, लेकिन मुझे किसी भी तरह से परवाह नहीं है। – mlissner

+0

दिनांक के साथ 1.5 यह निश्चित रूप से काम करता है, मेरा बुरा। लेकिन मैं अभी भी मैटएच शॉन चिन की तुलना में एक क्लीनर/तेज दृष्टिकोण के साथ पुरस्कार देना चाहूंगा ... – Dieter

उत्तर

15

इसे देखते हुए, कम से कम हैकी तरीका डेटूइल parser को एक अस्पष्ट-एकाधिक विकल्प के लिए संशोधित करना होगा।

parser._parse आपकी स्ट्रिंग लेता है, इसे _timelex के साथ टोकन करता है और फिर parserinfo में परिभाषित डेटा के साथ टोकन की तुलना करता है।

Here, यदि टोकन parserinfo में कुछ भी मेल नहीं खाता है, तो पार्स विफल रहेगा जब तक fuzzy सत्य नहीं है।

मेरा सुझाव है कि आप गैर-मैचों की अनुमति देते हैं, जबकि आपके पास कोई संसाधित समय टोकन नहीं है, फिर जब आप एक गैर-मिलान करते हैं, उस बिंदु पर पार्स किए गए डेटा को संसाधित करते हैं और फिर समय टोकन की तलाश शुरू करते हैं।

बहुत अधिक प्रयास नहीं करना चाहिए।


अद्यतन

के लिए अपने पैच में लुढ़का पाने के लिए प्रतीक्षा करते समय ...

यह एक छोटे से hacky है, पुस्तकालय में गैर सरकारी कार्यों का उपयोग करता है, लेकिन नहीं करता है पुस्तकालय को संशोधित करने की आवश्यकता नहीं है और परीक्षण-और-त्रुटि नहीं है। यदि आपके पास कोई अकेला टोकन है जो फ़्लोट्स में बदल सकता है तो आपके पास झूठी सकारात्मक हो सकती है। आपको कुछ और परिणाम फ़िल्टर करने की आवश्यकता हो सकती है।

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 प्रकट होता है के लिए

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

अद्यतन python3 साथ संगतता के लिए लिखा गया था और एक "संगतता" पुस्तकालय six कहा जाता है का उपयोग करता है किया जाना है। इसके साथ कुछ सही नहीं है और यह पाठ के रूप में str ऑब्जेक्ट्स का इलाज नहीं कर रहा है।

यह समाधान डेट्यूटिल 2 के साथ काम करता है।1 यदि आप यूनिकोड के रूप में या फ़ाइल की तरह वस्तुओं के रूप में तार पारित:

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

आप, parserinfo पर विकल्प सेट एक parserinfo का दृष्टांत और यह पार्सर वस्तु को पास करना चाहते हैं। E.g:

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 से थोड़ा भटक जब खाली/अज्ञात तार के साथ काम कर शायद लायक है। डेट्यूटिल वर्तमान दिन लौटाएगा, जबकि parse_multiple एक खाली सूची लौटाता है, जो आईएमएचओ है, जो उम्मीद करेगा।

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

पीएस बस MattH's updated answer देखा जो कुछ बहुत समान करता है।

+0

यह मैटएच के सुझाव से शुरू में अधिक विश्वसनीय लग रहा था, लेकिन प्रदर्शन बड़े परीक्षणों पर आश्चर्यजनक था (आश्चर्य की बात नहीं)। फ़िर भी सहायता के लिए धन्यवाद! – mlissner

+0

@mlissner आपका स्वागत है। यह हल करने के लिए एक मजेदार समस्या थी। इतनी ज्यादा कि मैं कल रात उस पर सोच रहा था और जो कुछ मुझे विश्वास है वह एक बेहतर समाधान है। अद्यतन उत्तर देखें। –

0

मुझे लगता है कि यदि आप किसी शब्द में "शब्द" डालते हैं, तो यह चाल चलनी चाहिए। इसके साथ आप यह सत्यापित कर सकते हैं कि यह दिनांक या नहीं है, और एक चर में डाल दिया गया है।

एक बार आपके पास तिथि होने के बाद आपको datetime library लाइब्रेरी का उपयोग करना चाहिए।

0

एक रेगेक्स पैटर्न क्यों नहीं लिख रहा है जिसमें सभी संभव रूपों को शामिल किया गया है जिसमें एक तिथि दिखाई दे सकती है, और फिर टेक्स्ट का पता लगाने के लिए रेगेक्स लॉन्च कर रहा है? मुझे लगता है कि एक स्ट्रिंग में एक तारीख व्यक्त करने के लिए दर्जनों दर्जनों शिष्टाचार नहीं हैं।

समस्या सिर्फ "ईस्टर पर" एक तारीख आप पार्स करने के लिए चाहता हूँ की तारीख के भाव