2010-10-03 31 views
22

Tôi cần có khả năng nhận biết chuỗi ngày tháng. Không quan trọng nếu tôi không thể phân biệt giữa tháng và ngày (ví dụ: 12/12/10), tôi chỉ cần phân loại chuỗi là ngày, thay vì chuyển đổi nó thành đối tượng Ngày tháng. Vì vậy, đây thực sự là một phân loại hơn là phân tích vấn đề.Nhận dạng chuỗi ngày tùy ý

tôi sẽ có đoạn văn bản như:

"bla bla bla bla 12 ngày 09 tháng 1 bla bla bla 01/04/10 bla bla bla"

và tôi cần có khả năng nhận ra ranh giới bắt đầu và kết thúc cho mỗi chuỗi ngày bên trong.

Tôi đã tự hỏi liệu có ai biết về bất kỳ thư viện java nào có thể làm điều này không. My-fu của tôi đã không đưa ra bất cứ điều gì cho đến nay.

CẬP NHẬT: Tôi cần có khả năng nhận ra tập hợp các cách biểu diễn ngày có thể rộng nhất có thể. Tất nhiên, giải pháp ngây thơ có thể là viết một câu lệnh if cho mọi định dạng có thể tưởng tượng, nhưng cách tiếp cận nhận dạng mẫu , với mô hình được đào tạo, lý tưởng là những gì tôi theo sau.

+0

tôi đã xóa câu trả lời của tôi sau khi (* thở hổn hển *) thực sự đọc tài liệu cho DateFormat :) – Dave

+0

Oh! Và bạn đã có một cái nhìn về Lịch và SimpleDateFormat và các phương thức Ngày không được chấp nhận và .... :-) –

+2

Nếu bạn đang tìm cách nhận ra các ngày từ tất cả các ngôn ngữ, đừng quên tính đến các ký tự phân cách khác nhau như trong 30.12 .2010 và 2010 年 12 月 30 日 – oksayt

Trả lời

5

Sử dụng JChronic

Bạn có thể muốn sử dụng DateParser2 từ edu.mit.broad.genome.utils gói.

+0

Có tải xuống cho DateParser2 không? – Joel

+0

Dường như là một phần của toàn bộ cơ sở mã phân tích bộ gen. Có một liên kết tải xuống có sẵn trên trang chủ (http://www.broadinstitute.org/gsea/index.jsp) nhưng trước tiên nó yêu cầu đăng ký miễn phí. – corriganjc

+0

@Puspendu: Đã thử JChronic. Trông khá tốt. – Joel

0

Ngày thường là các ký tự được phân tách bằng dấu gạch chéo ngược/gạch chéo hoặc dấu gạch ngang. Bạn đã xem xét một biểu thức chính quy?

Tôi giả sử bạn đang không tìm cách để phân loại số ngày của các loại Chủ Nhật 3 tháng 10, 2010 và vân vân

+0

Có, tôi là. BẤT CỨ định dạng ngày. – Joel

+0

Bạn sai một cách bất thường. Có cả một thế giới bên ngoài và tôi sợ rằng hầu hết các quốc gia không sử dụng dấu gạch chéo làm dấu phân cách ngày. –

0

Tôi không biết về bất kỳ thư viện mà có thể làm được điều này nhưng viết riêng của bạn sẽ không được vô cùng cứng. Giả sử ngày của bạn được định dạng với các dấu gạch chéo như 12/12/12 thì bạn có thể xác minh bạn có ba '\' s. Bạn có thể nhận được nhiều kỹ thuật hơn và có nó kiểm tra các giá trị ở giữa các dấu gạch chéo. Ví dụ: nếu bạn có:

30/12/10

Sau đó, bạn biết rằng 30 là ngày và 12 là tháng. Tuy nhiên, nếu bạn nhận được 30/30/10 bạn biết rằng mặc dù ti có định dạng chính xác, nó không thể là một ngày vì không có '30' tháng.

1

Có thể bạn nên sử dụng cụm từ thông dụng?

Hy vọng này sẽ làm việc cho định dạng mm-dd-yyyy:

^(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d$

đây (0[1-9]|1[012]) phù hợp với tháng 00..12, (0[1-9]|[12][0-9]|3[01]) phù hợp với một ngày 00..31 và (19|20)\d\d phù hợp với một năm.

Các trường có thể được xóa bằng dấu gạch ngang, dấu gạch chéo hoặc dấu chấm.

Kính trọng, Serge

+0

Có vô số cách để biểu thị ngày tháng. Mặc dù tôi có thể sử dụng heuristics đơn giản một phân loại có thể mạnh mẽ hơn. Tôi cần phải nhận dạng bất kỳ định dạng ngày nào. – Joel

+0

@ Sau đó có thể bạn có thể chia chuỗi bằng [- /.] regex và sau đó đảm bảo rằng nó có 3 trường và mỗi trường trong số đó tính toán một trong các biểu thức cho ngày (từ 0 đến 30), tháng (từ 0 đến 12) và năm (19xx/20xx hoặc chỉ xx)? – zserge

+0

Có, có vẻ như một cách tiếp cận tốt - để chia nhỏ mọi ký tự không phải chữ số và sau đó kiểm tra từng trường một cách độc lập và đảm bảo rằng bạn có ít nhất một ứng cử viên cho mỗi tháng, ngày và năm. – Joel

0

Tôi không biết về bất kỳ thư viện mà thực hiện điều này một trong hai. Tôi sẽ đề nghị một kết hợp các hàm đệ quy lồng nhau và các biểu thức thông thường (rất nhiều) để phù hợp với chuỗi và cố gắng đưa ra một dự đoán tốt nhất để xem nó có thể là một ngày hay không.Ngày có thể được viết bằng nhiều cách khác nhau, một số người có thể viết chúng ra là "Chủ nhật, ngày 3 tháng 10 năm 2010" hoặc "Chủ nhật, ngày 3 tháng 10 năm 2010" hoặc "10/03/2010" hoặc "ngày 10/3/2010" và một loạt các cách khác nhau (thậm chí nhiều hơn nếu bạn đang xem xét ngày trong các ngôn ngữ/nền văn hóa khác).

0

Bạn luôn có thể kiểm tra xem có hai ký tự '/' trong chuỗi không.

public static boolean isDate(){ 
    String date = "12/25/2010"; 
    int counter = 0; 
    for(int i=0; i<date.length(); i++){ 
      if ("\/-.".indexOf(date.charAt(i)) != -1) //Any symbol can be used. 
       counter++; 
    } 
    if(counter == 2) //If there are two symbols in the string, 
      return true; //Return true. 
    else 
      return false; 
} 

Bạn có thể làm điều gì đó tương tự để kiểm tra xem mọi thứ khác có phải là số nguyên không.

+1

Châu Âu sử dụng dấu chấm thay vì dấu gạch chéo, thường là. –

1

Hầu như không thể nhận ra tất cả các định dạng ngày có thể là ngày sử dụng thuật toán "chuẩn". Đó là chỉ vì có rất nhiều người trong số họ.

Chúng tôi, con người có khả năng làm điều đó chỉ vì chúng tôi biết được điều gì đó giống như ngày 2010-03-31 giống với ngày. Nói cách khác, tôi sẽ đề nghị sử dụng thuật toán Machine Learning và dạy chương trình của bạn để nhận ra chuỗi ngày hợp lệ. Với Google Prediction API nên khả thi.

Hoặc bạn có thể sử dụng Cụm từ thông dụng như được đề xuất ở trên, để phát hiện một số chứ không phải tất cả các định dạng ngày.

+0

Ngoài ra tôi nghĩ rằng dương tính giả sẽ là một vấn đề lớn! ví dụ. với điểm số 10 tháng 1 được xếp hạng ngày 2 tháng 5 và ngày 3 tháng 6 7. –

2

Tôi chắc rằng các nhà nghiên cứu ở số information extraction đã xem xét vấn đề này, nhưng tôi không thể tìm thấy bài báo.

Một điều bạn có thể thử làm là tiến trình hai bước. (1) sau khi thu thập càng nhiều dữ liệu càng tốt, hãy trích xuất các tính năng, một số tính năng cần lưu ý: số lượng xuất hiện trong chuỗi, số từ 1-31 xuất hiện trong chuỗi, số lượng từ 1 đến 1 12 xuất hiện trong chuỗi, số tháng tên xuất hiện trong chuỗi, v.v. (2) tìm hiểu từ các tính năng sử dụng một số loại phương pháp phân loại nhị phân (SVM chẳng hạn) và cuối cùng (3) khi một chuỗi mới đi kèm, trích xuất các tính năng và truy vấn SVM để dự đoán.

+0

+1, SVM có thể là công cụ học tập hợp lý. – Joel

5

Bạn có thể lặp tất cả các định dạng ngày có sẵn trong Java:

for (Locale locale : DateFormat.getAvailableLocales()) { 
    for (int style = DateFormat.FULL; style <= DateFormat.SHORT; style ++) { 
     DateFormat df = DateFormat.getDateInstance(style, locale); 
     try { 
       df.parse(dateString); 
       // either return "true", or return the Date obtained Date object 
     } catch (ParseException ex) { 
      continue; // unperasable, try the next one 
     } 
    } 
} 

Tuy nhiên điều này sẽ không chiếm bất kỳ định dạng ngày tùy chỉnh.

+0

Có, đã xem xét điều này, nhưng nó cuối cùng là một danh sách hữu hạn. – Joel

4

tôi đã làm nó với một regex khổng lồ (tự tạo):

public static final String DATE_REGEX = "\b([0-9]{1,2} ?([\\-/\\\\] ?[0-9]{1,2} ?| (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?)([\\-/\\\\]? ?('?[0-9]{2}|[0-9]{4}))?)\b"; 
public static final Pattern DATE_PATTERN = Pattern.compile(DATE_REGEX, Pattern.CASE_INSENSITIVE); // Case insensitive is to match also "mar" and not only "Mar" for March 

public static boolean containsDate(String str) 
{ 
    Matcher matcher = pattern.matcher(str); 
    return matcher.matches(); 
} 

này phù hợp với ngày sau:

06 Sep 2010 
12-5-2005 
07 Mar 95 
30 DEC '99 
11\9\2001 

Và không này:

444/11/11 
bla11/11/11 
11/11/11blah 

Nó cũng phù hợp với ngày giữa các biểu tượng như [], (), ,:

Yesterday (6 nov 2010) 

Nó phù hợp với số ngày mà không năm:

Yesterday, 6 nov, was a rainy day... 

Nhưng nó phù hợp với:

86-44/1234 
00-00-0000 
11\11/11 

Và điều này không giống không nữa giống như một ngày. Nhưng đây là một cái gì đó bạn có thể giải quyết bằng cách kiểm tra xem các con số là giá trị có thể cho một tháng, ngày, năm.

1

Điều tôi sẽ làm là tìm kiếm các đặc tính ngày tháng, chứ không phải là ngày tháng. Ví dụ: bạn có thể tìm kiếm dấu gạch ngang, (để nhận ngày tháng của biểu mẫu 1/1/1001), dấu gạch ngang (1 - 1 - 1001), tên tháng và chữ viết tắt (Jan 1 1001 hoặc January 1 1001). Khi bạn nhận được một hit cho những, thu thập các từ gần đó (2 trên mỗi bên nên được tốt) và lưu trữ đó trong một mảng của chuỗi. Một khi bạn đã quét tất cả đầu vào, hãy kiểm tra mảng chuỗi này với một hàm sẽ đi sâu hơn một chút và kéo ra các chuỗi ngày tháng thực tế, bằng cách sử dụng các phương thức được tìm thấy ở đây. Điều quan trọng là chỉ nhận được những ngày chung xuống đến một mức độ quản lý được.

5

Quy định có thể giúp bạn trong việc tìm kiếm của bạn:

  1. Thực hiện hoặc tìm thấy một số loại của một cơ sở dữ liệu với các từ được biết đến phù hợp với tháng. Tên viết tắt và đầy đủ, như Jan hoặc January. Trong khi tìm kiếm, nó phải là trường hợp không nhạy cảm, bởi vì fEBruaRy cũng là một tháng, mặc dù người gõ nó phải đã say. Nếu bạn có kế hoạch tìm kiếm các tháng không phải tiếng Anh, một cơ sở dữ liệu cũng là cần thiết, bởi vì không có heuristic sẽ tìm ra rằng "Wrzesień" là đánh bóng cho tháng chín.
  2. Chỉ có bằng tiếng Anh, hãy xem ordinal numbers và cũng tạo cơ sở dữ liệu cho các con số từ 1 đến 31. Những điều này sẽ hữu ích cho ngày và tháng. Nếu bạn muốn sử dụng phương pháp này cho các ngôn ngữ khác, thì bạn sẽ phải tự nghiên cứu.
  3. Một lần nữa, chỉ có tiếng Anh, hãy kiểm tra "Anno Domini" và "Before Christ", tức là, AD và BC tương ứng. Họ cũng có thể ở dạng A.D. và B.C.
  4. Liên quan đến các số sẽ đại diện cho ngày, tháng và năm, bạn phải biết giới hạn của mình ở đâu. Có phải là 0-9999 hay cao hơn? Đó là, bạn có muốn tìm kiếm các ngày đại diện cho các năm vượt quá năm 9999 không? Nếu không, thì các chuỗi có 1-4 chữ số liên tiếp là các số đoán tốt cho một ngày, tháng hoặc năm hợp lệ.
  5. Ngày và tháng có một hoặc hai chữ số. Các số 0 đứng đầu có thể chấp nhận được, vì vậy các chuỗi có định dạng 0*, trong đó * có thể là 1-9 đều có thể chấp nhận được.
  6. Máy tách có thể khó khăn, nhưng nếu bạn không cho phép định dạng không nhất quán như 10/20 \ 1999, thì bạn sẽ tiết kiệm cho mình rất nhiều đau buồn. Điều này là do 10 * 20 * 1999 có thể là ngày hợp lệ, với * thường là một phần tử của tập hợp {-,_, ,:,/,\,.,','}, nhưng có thể là * là sự kết hợp của 2 hoặc 3 phần tử được đề cập. Một lần nữa, bạn phải chọn các dấu phân cách có thể chấp nhận được. 10? 20? 1999 có thể là một ngày hợp lệ cho ai đó với một cảm giác thanh lịch kỳ lạ. 10/20/1999 cũng có thể là một ngày hợp lệ, nhưng 10_/20_/1999 sẽ là một ngày rất lạ.
  7. Có những trường hợp không có dấu phân tách. Ví dụ: 10Jan1988. Những trường hợp này sử dụng các từ từ 1.
  8. Có những trường hợp đặc biệt, như ngày 28 hoặc 29 tháng 2, tùy thuộc vào năm nhuận. Ngoài ra, tháng với 30 hoặc 31 ngày.

Tôi nghĩ đây là đủ để phân loại "ngây thơ", chuyên gia ngôn ngữ học có thể giúp bạn nhiều hơn.

Bây giờ, một ý tưởng cho thuật toán của bạn. Tốc độ không quan trọng. Có thể có nhiều lần vượt qua cùng một chuỗi. Tối ưu hóa khi nó bắt đầu quan trọng. Khi bạn nghi ngờ rằng bạn đã tìm thấy một chuỗi ngày, lưu trữ nó ở đâu đó "an toàn" trong một ListOfPossibleDates và thực hiện kiểm tra một lần nữa, với các quy tắc cứng nhắc hơn bằng cách sử dụng các kết hợp từ 1. đến 8. Khi bạn tin rằng chuỗi ngày hợp lệ, hãy cho nó ăn đến lớp Date để xem nó có thực sự hợp lệ hay không.Ngày 32 tháng 3 năm 1999 không hợp lệ, khi bạn chuyển đổi sang định dạng Date sẽ hiểu.

Một mẫu định kỳ quan trọng là trông giống và trông nom. Khi bạn tin rằng một thực thể hợp lệ (ngày, tháng, năm) được tìm thấy, bạn sẽ phải thấy những gì nằm đằng sau và sau đó. Một cơ chế dựa trên stack hoặc đệ quy có thể giúp đỡ ở đây.

bước:

  1. Tìm kiếm chuỗi của bạn cho từ từ quy tắc 1. Nếu bạn tìm thấy bất kỳ trong số họ, lưu ý vị trí đó. Lưu ý tháng. Bây giờ, hãy đi vài ký tự phía sau và một vài bước phía trước để xem điều gì đang chờ bạn. Nếu không có dấu cách nào trước và sau tháng của bạn, và có những con số, như trong quy tắc 7., hãy kiểm tra chúng về tính hợp lệ. Nếu một trong số họ đại diện cho một ngày (phải là 0-31) và một năm khác (phải là 0-9999, có thể với AD hoặc BC), bạn có một ứng cử viên. Nếu có cùng dấu phân cách trước và sau, hãy tìm các quy tắc từ 6. Luôn nhớ rằng bạn phải chắc chắn rằng một kết hợp hợp lệ tồn tại. vì vậy, 32Jan1999 sẽ không làm.
  2. Tìm chuỗi của bạn cho các từ tiếng Anh khác, từ quy tắc 2. và 3. Lặp lại tương tự như trong bước 1.
  3. Tìm kiếm dấu phân cách. Không gian trống sẽ là khó khăn nhất. Cố gắng tìm chúng theo cặp. Vì vậy, nếu bạn có một "/" trong chuỗi của bạn, tìm một số khác và xem những gì họ có inbetween. Nếu bạn tìm thấy một sự kết hợp của phân cách, để cùng một điều. Ngoài ra, hãy sử dụng thuật toán từ bước 2.
  4. Tìm kiếm chữ số. Các giá trị hợp lệ là 0-9999 với các số 0 hàng đầu được cho phép. Nếu bạn tìm thấy dấu phân tách, hãy tìm các dấu tách như trong bước 3.

Vì có vô số khả năng, bạn sẽ không thể bắt được tất cả. Một khi bạn đã tìm thấy một mô hình mà bạn tin rằng có thể xảy ra một lần nữa, lưu trữ nó ở đâu đó và bạn có thể sử dụng nó như là một regex để đi qua các chuỗi khác.

Hãy lấy ví dụ của bạn, "bla bla bla bla 12 Jan 09 bla bla bla 01/04/10 bla bla bla". Sau khi bạn trích xuất ngày đầu tiên, 12 Jan 09, sau đó sử dụng phần còn lại của chuỗi đó ("bla bla bla 01/04/10 bla bla bla") và áp dụng tất cả các bước trên một lần nữa. Bằng cách này bạn sẽ chắc chắn bạn đã không bỏ lỡ bất cứ điều gì.

Tôi hy vọng những đề xuất này sẽ có ít nhất một số trợ giúp. Nếu không tồn tại một thư viện để làm tất cả các bước bẩn (và nhiều hơn nữa) cho bạn, sau đó bạn có một con đường khó khăn phía trước của bạn. Chúc may mắn!

3

phân tích cú pháp ngày Rất tốt trong java là Natty, bạn có thể thử nó here

2

Dưới đây là một ví dụ Natty đơn giản:

import com.joestelmach.natty.*; 

List<Date> dates =new Parser().parse("Start date 11/30/2013 , end date Friday, Sept. 7, 2013").get(0).getDates(); 
     System.out.println(dates.get(0)); 
     System.out.println(dates.get(1)); 

//output: 
     //Sat Nov 30 11:14:30 BDT 2013 
     //Sat Sep 07 11:14:30 BDT 2013 
Các vấn đề liên quan