2011-08-11 36 views
16

Tôi có một chuỗi có nhiều giá trị ngày trong đó và tôi muốn phân tích tất cả chúng ra. Chuỗi là ngôn ngữ tự nhiên, vì vậy điều tốt nhất tôi đã tìm thấy cho đến nay là dateutil.Cách phân tích nhiều ngày từ một khối văn bản bằng Python (hoặc một ngôn ngữ khác)

Thật không may, nếu một chuỗi có nhiều giá trị ngày trong nó, dateutil ném một lỗi:

>>> 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 

Bất kỳ suy nghĩ về cách để phân tích tất cả các ngày từ một chuỗi dài? Lý tưởng nhất, một danh sách sẽ được tạo ra, nhưng tôi có thể tự mình xử lý nếu tôi cần.

Tôi đang sử dụng Python, nhưng tại thời điểm này, các ngôn ngữ khác có thể là OK, nếu họ hoàn thành công việc.

PS - Tôi đoán tôi có thể phân tách đệ quy tệp đầu vào ở giữa và thử, thử lại cho đến khi nó hoạt động, nhưng đó là địa ngục.

+0

Trong chuỗi mẫu của bạn, bạn đang xem "ngày phục sinh" là ngày bạn muốn phân tích cú pháp? – MattH

+0

Không. Đã được thử nghiệm để xem nếu nó làm việc, nhưng tôi không quan tâm quá nhiều một trong hai cách. – mlissner

+0

Với DateUtil 1.5 nó làm việc tất nhiên, xấu của tôi. Nhưng tôi vẫn muốn trao giải với cách tiếp cận sạch hơn/nhanh hơn MattH Shawn Chin ... – Dieter

Trả lời

15

Nhìn vào nó, cách ít tốn kém nhất là sửa đổi dateutil parser để có tùy chọn nhiều mờ.

parser._parse lấy chuỗi của bạn, mã hóa nó bằng _timelex và sau đó so sánh mã thông báo với dữ liệu được xác định trong parserinfo.

Here, nếu mã thông báo không khớp với bất kỳ thứ gì trong parserinfo, phân tích cú pháp sẽ không thành công trừ khi fuzzy là True.

Điều tôi đề nghị bạn cho phép không khớp trong khi bạn không có mã thông báo thời gian đã xử lý, khi bạn nhấn không khớp, xử lý dữ liệu được phân tích cú pháp tại điểm đó và bắt đầu tìm mã thông báo thời gian một lần nữa.

Không nên nỗ lực quá nhiều.


Cập nhật

Trong khi bạn đang chờ đợi bản vá của bạn để có được cuộn trong ...

Đây là một chút hacky, sử dụng chức năng ngoài công lập trong thư viện, nhưng doesn không yêu cầu sửa đổi thư viện và không phải là thử và lỗi. Bạn có thể có dương tính giả nếu bạn có bất kỳ thẻ duy nhất có thể được biến thành phao. Bạn có thể cần phải lọc kết quả một số chi tiết.

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) 

Sản lượng:

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

Cập nhật cho Dieter

Dateutil 2.1 dường như được viết để tương thích với python3 và sử dụng một "tương thích" thư viện gọi six. Một cái gì đó không đúng với nó và nó không xử lý các đối tượng str làm văn bản.

Giải pháp này hoạt động với dateutil 2.1 nếu bạn vượt qua chuỗi như unicode hoặc như các đối tượng tập tin giống như:

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

Nếu bạn muốn thiết lập tùy chọn trên parserinfo, nhanh chóng một parserinfo và vượt qua nó đến đối tượng phân tích cú pháp. Ví dụ:

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

Có lẽ giải pháp hiệu quả nhất. +1. Tất nhiên, việc sửa đổi thư viện chính nó sẽ làm cho việc triển khai/bảo trì khó hơn một chút trừ khi những thay đổi được hấp thụ vào nguồn chính thức. –

+0

Đây là một câu trả lời tuyệt vời - cho đến nay là tốt nhất tôi từng nhận được trên SO. Sẽ kiểm tra nó ra và cho bạn biết làm thế nào nó đi. Cảm ơn! – mlissner

+0

Điều này hoạt động khá tốt. Nó có TypeErrors và ValueErrors mà tôi cần để nắm bắt, và nhiều sai lầm tích cực. Các lỗi rất dễ nắm bắt, và tôi loại bỏ các mặt tích cực sai bởi nuking bất cứ điều gì từ năm hiện tại (corpus của tôi chỉ có ngày cũ). Cảm ơn một lần nữa. – mlissner

5

Khi tôi ngoại tuyến, tôi đã bị làm phiền bởi câu trả lời tôi đăng ở đây hôm qua. Có nó đã làm công việc, nhưng nó là không cần thiết phức tạp và cực kỳ không hiệu quả.

Đây là phiên bản back-of-the-phong bì mà nên làm một công việc tốt hơn nhiều!

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)] 

sử dụng Ví dụ:

>>> 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)] 

Có lẽ đáng chú ý là hành vi của nó lệch chút ít so với dateutil.parser.parse khi giao dịch với trống dây/không rõ. Dateutil sẽ trả về ngày hiện tại, trong khi parse_multiple trả về một danh sách rỗng, IMHO, là cái mà người ta mong đợi.

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

P.S. Chỉ cần phát hiện MattH's updated answer làm điều gì đó rất giống nhau.

+0

Điều này dường như đáng tin cậy hơn ban đầu so với gợi ý của MattH, nhưng hiệu suất là vô cùng trong các thử nghiệm lớn hơn (không đáng ngạc nhiên). Cảm ơn sự giúp đỡ của bạn! – mlissner

+0

@mlissner bạn được chào đón. Đó là một vấn đề thú vị để giải quyết. Vì vậy, nhiều đến nỗi tôi đã suy nghĩ về nó tối qua và nghĩ ra những gì tôi tin là một giải pháp tốt hơn. Xem câu trả lời cập nhật. –

0

Tôi nghĩ rằng nếu bạn đặt các "chữ" trong một mảng, nó sẽ làm các trick. Với điều đó, bạn có thể xác minh xem đó có phải là ngày hay không và đặt vào một biến.

Một khi bạn có ngày bạn nên sử dụng datetime library thư viện.

0

Tại sao không viết một mẫu biểu thức chính bao gồm tất cả các hình thức có thể, trong đó một ngày có thể xuất hiện, và sau đó tung ra regex để khám phá những văn bản không? Tôi cho rằng không có hàng chục cách cư xử để diễn tả một ngày trong một chuỗi.

Vấn đề duy nhất là thu thập tối đa các biểu thức của ngày

Các vấn đề liên quan