24

日付文字列を認識できる必要があります。月と日付を区別できないかどうかは関係ありません(例:12/12/10)。文字列をDateオブジェクトに変換するのではなく、日付として分類する必要があります。したがって、これは実際には解析の問題ではなく分類です。

次のようなテキストがあります。

「 blablablabla 12 Jan 09 bla bla bla 01/04/10 blablabla

内の各日付文字列の開始境界と終了境界を認識できる必要があります。

これを実行できるJavaライブラリを誰かが知っているかどうか疑問に思いました。私のgoogle-fuは今のところ何も思い付いていません。

更新:日付を表すための可能な限り幅広い方法のセットを認識できる必要があります。もちろん、単純な解決策は、考えられるすべての形式に対してifステートメントを記述することかもしれませんが、訓練されたモデルを使用したパターン認識アプローチは、理想的には私が求めているものです。

4

15 に答える 15

6

JChronicを使用する

edu.mit.broad.genome.utilsパッケージのDateParser2を使用することをお勧めします。

于 2010-11-08T18:35:02.547 に答える
5

あなたの探求に役立つかもしれないルール:

  1. 月に一致する既知の単語を含むある種のデータベースを作成または検索します。Janまたはのような省略形およびフルネームJanuary。fEBruaRyも1か月であるため、検索中は大文字と小文字を区別しない必要がありますが、入力する人は酔っていたに違いありません。英語以外の月を検索する場合は、データベースも必要です。これは、「Wrzesień」が9月に洗練されていることをヒューリスティックが検出しないためです。
  2. 英語のみの場合は、序数を確認し、1から31までの数字のデータベースを作成します。これらは数日から数か月間役立ちます。このアプローチを他の言語で使用したい場合は、独自の調査を行う必要があります。
  3. 繰り返しになりますが、英語のみで、「AnnoDomini」と「BeforeChrist」、つまりそれぞれADとBCを確認してください。それらは、ADおよびBCの形式にすることもできます
  4. 日、月、年を表す数値自体に関しては、制限がどこにあるかを知っている必要があります。0-9999以上ですか?つまり、9999年以降の年を表す日付を検索しますか?いいえの場合、1〜4桁の連続する文字列は、有効な日、月、または年の適切な推測です。
  5. 日と月は1桁または2桁です。先行ゼロは許容されるため0*、*が1〜9の形式の文字列が許容されます。
  6. セパレーターは扱いにくい場合がありますが、10/20 \ 1999のように一貫性のないフォーマットを許可しない場合は、多くの苦痛を軽減できます。これは、10 * 20 * 1999が有効な日付であり、*は通常セットの1つの要素である{-,_, ,:,/,\,.,','}可能性があるためですが、*は上記のセットの2つまたは3つの要素の組み合わせである可能性があります。ここでも、許容できるセパレータを選択する必要があります。1999年10月20日は、奇妙な優雅さを持っている人にとって有効な日付になる可能性があります。10/20/1999も有効な日付になる可能性がありますが、10_ / 20_/1999は非常に奇妙な日付になります。
  7. セパレータがない場合があります。例:10Jan1988。これらのケースでは、1の単語を使用します。
  8. うるう年によっては、2月28日や29日などの特殊なケースがあります。また、30日または31日の月。

これらは「ナイーブ」分類には十分だと思います。言語学者の専門家がもっと役立つかもしれません。

さて、あなたのアルゴリズムのアイデア。速度は関係ありません。同じ文字列に複数のパスがある可能性があります。重要になり始めたら最適化します。日付文字列を見つけたListOfPossibleDatesと思われる場合は、aの「安全な」場所に保存し、1から8までの組み合わせを使用して、より厳密なルールでもう一度検査を行います。日付文字列が有効であると思われる場合は、にフィードします。Dateクラスが本当に有効かどうかを確認します。1999年3月32日は、理解できる形式に変換すると無効になりますDate

重要な繰り返しパターンの1つは、後読みと見回しです。有効なエンティティ(日、月、年)が見つかったと思われる場合は、背後にあるものとその後にあるものを確認する必要があります。ここでは、スタックベースのメカニズムまたは再帰が役立つ場合があります。

手順:

  1. ルール1の単語を文字列で検索します。単語が見つかった場合は、その場所をメモします。月に注意してください。さて、何文字か後ろに、数文字先に進んで、何があなたを待っているかを見てください。月の前後にスペースがなく、ルール7のように数字がある場合は、それらの有効性を確認してください。それらの1つが1日(0〜31である必要があります)および他の1年(0〜9999である必要があり、おそらくADまたはBCを含む)を表す場合、1つの候補があります。前後に同じ区切り文字がある場合は、6のルールを探します。有効な組み合わせが存在することを確認する必要があることを常に忘れないでください。したがって、32Jan1999は機能しません。
  2. ルール2と3から、他の英語の単語を文字列で検索します。手順1と同様に繰り返します。
  3. セパレータを検索します。空のスペースが最もトリッキーになります。それらをペアで見つけてみてください。したがって、文字列に「/」が1つある場合は、別の「/」を見つけて、その間に何があるかを確認します。セパレーターの組み合わせを見つけた場合は、同じことです。また、ステップ2のアルゴリズムを使用します。
  4. 数字を検索します。有効なものは0〜9999で、先行ゼロが許可されます。見つかった場合は、手順3のようにセパレータを探します。

文字通り無数の可能性があるので、それらすべてを捕まえることはできません。もう一度発生する可能性があると思われるパターンを見つけたら、それをどこかに保存して、他の文字列を渡すための正規表現として使用できます。

あなたの例を見てみましょう、"bla bla bla bla 12 Jan 09 bla bla bla 01/04/10 bla bla bla"。最初の日付を抽出した後、、12 Jan 09次にその文字列の残りの部分("bla bla bla 01/04/10 bla bla bla")を使用して、上記のすべての手順をもう一度適用します。このようにして、何も見逃していないことを確認できます。

これらの提案が少なくともある程度の助けになることを願っています。これらすべての汚い(そしてそれ以上の)ステップを実行するためのライブラリが存在しない場合、あなたはあなたの前に困難な道を歩んでいます。幸運を!

于 2010-11-10T21:25:35.227 に答える
5

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

ただし、これはカスタムの日付形式を考慮しません。

于 2010-10-19T13:25:16.907 に答える
4

私は巨大な正規表現(自己作成)でそれを行いました:

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();
}

これは次の日付と一致します。

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

そしてこれではありません:

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

また、、、:など[]の記号間の日付にも一致します。(),

Yesterday (6 nov 2010)

年のない日付と一致します。

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

しかし、それは一致します:

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

そして、これはもはや日付のようには見えません。しかし、これは、数値が月、日、年の可能な値であるかどうかを確認することで解決できるものです。

于 2010-11-07T14:30:47.603 に答える
3

これは簡単な簡単な例です:

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
于 2013-12-02T10:14:31.463 に答える
2

情報抽出の研究者はこの問題を見たと思いますが、論文が見つかりませんでした。

あなたが試すことができる一つのことは、2段階のプロセスとしてそれを行うことです。(1)できるだけ多くのデータを収集した後、特徴、頭に浮かぶいくつかの特徴を抽出します:文字列に表示される数字の数、文字列に表示される1〜31の数字の数、1〜の数字の数文字列に表示される12、文字列に表示される月の数など。(2)ある種の二項分類法(たとえばSVM)を使用して特徴から学び、最後に(3)新しい文字列が来たら、特徴を抽出し、SVMに予測を問い合わせます。

于 2010-10-17T08:27:52.830 に答える
2

java.time

を使用して、必要な数のカスタムパターンを指定できますDateTimeFormatter。パターンを角かっこで囲んでオプションとして指定するだけです。DateTimeFormatterBuilder大文字と小文字を区別しない解析、デフォルトで欠落しているユニット(eg HOUR_OF_DAY)など、さらに多くの機能を提供します。

デモ:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        // DateTimeFormatter parser = DateTimeFormatter.ofPattern("[M/d/uu[ H:m]][d MMM u][M.d.u][E MMM d, u]", Locale.ENGLISH);
        final DateTimeFormatter parser = new DateTimeFormatterBuilder()
                    .parseCaseInsensitive() // parse in case-insensitive manner
                    .appendPattern("[M/d/uu[ H:m]][d MMM u][M.d.u][E MMM d, u]")
                    .toFormatter(Locale.ENGLISH);
        
        // Test
        Stream.of(
                    "Thu Apr 1, 2021",
                    "THU Apr 1, 2021",
                    "01/06/10",
                    "1 Jan 2009",
                    "1.2.2010",
                    "asdf"
                ).forEach(s -> {
                    try {
                        LocalDate.parse(s, parser);
                        System.out.println(true);
                    } catch(DateTimeParseException e) {
                        System.out.println(false);
                    }
                });     
    }   
}

出力:

true
true
true
true
true
false

Trail: DateTimeから最新の日時APIの詳細をご覧ください。

于 2021-04-01T12:58:24.837 に答える
1

多分あなたは正規表現を使うべきですか?

うまくいけば、これはmm-dd-yyyy形式で機能します。

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

ここ(0[1-9]|1[012])では、月00..12に(0[1-9]|[12][0-9]|3[01])一致し、日付00..31に(19|20)\d\d一致し、年に一致します。

フィールドは、ダッシュ、スラッシュ、またはドットで指定できます。

よろしく、セルジュ

于 2010-10-03T17:33:52.587 に答える
1

「標準」アルゴリズムを使用して、考えられるすべての日付形式を日付として認識することは事実上不可能です。それは、それらが非常に多いからです。

私たち人間は、2010-03-31のようなものが日付に似ていることを知ったからといってそれを行うことができます。つまり、機械学習アルゴリズムを使用して、プログラムに有効な日付シーケンスを認識するように教えることをお勧めします。実現可能であるはずのGooglePredictionAPIを使用します。

または、上記のように正規表現を使用して、すべてではありませんが一部の日付形式を検出できます。

于 2010-10-04T11:52:18.610 に答える
1

私がすることは、日付自体ではなく、日付の特性を探すことです。たとえば、スラッシュ(1/1/1001の形式の日付を取得するため)、ダッシュ(1-1-1001)、月の名前、および略語(Jan11001またはJanuary11001)を検索できます。これらにヒットしたら、近くの単語を収集し(両側に2つあれば問題ありません)、それを文字列の配列に格納します。すべての入力をスキャンしたら、ここにあるメソッドを使用して、もう少し深く掘り下げて実際の日付文字列を引き出す関数でこの文字列配列を確認します。重要なのは、一般的な日付を管理可能なレベルに下げることです。

于 2010-11-10T08:28:51.883 に答える
1

これを確認して くださいhttps://github.com/zoho/hawking。ZOHOZIAチームによって開発されました。

Hawking Parserは、日付と時刻の情報を解析するためのJavaベースのNLPパーサーです。Heidel Time、SuTime、Natty Dateのような最も人気のあるパーサーは、明らかにルールベースです。そのため、コンテキスト、時制、複数の値などのより複雑な要素を考慮する必要がある日付/時刻情報の解析に苦労する傾向があります。

これを念頭に置いて、ホーキングパーサーはこれらの課題の多くに対処するように設計されており、他の利用可能な日付/時刻パーサーに比べて多くの明確な利点があります。

これはGPLv3の下でのオープンソースライブラリであり、最高のものです。なぜそれが最善であるかを知るために、詳細に説明しているこのブログをチェックしてください:https ://www.zoho.com/blog/general/zias-nlp-based-hawking-date-time-parser-is-now-open-source .html

PS:私はこのプロジェクトの開発者の一人です

于 2021-04-01T11:01:00.180 に答える
0

通常、日付は前後のスラッシュまたはダッシュで区切られた文字です。正規表現を検討しましたか?

2010年10月3日日曜日などのタイプの日付を分類することを検討していないと想定しています。

于 2010-10-03T17:32:40.703 に答える
0

これができるライブラリはありませんが、自分で作成するのはそれほど難しいことではありません。日付がすべてスラッシュでフォーマットされていると仮定すると、12/12/123つの「\」があることを確認できます。さらに技術的になり、スラッシュの間の値をチェックするようにできます。たとえば、次の場合:

2010年12月30日

次に、30が日で、12が月であることがわかります。ただし、30/30/10を取得した場合、tiの形式は正しいものの、「30」か月がないため、日付にすることはできません。

于 2010-10-03T17:33:05.820 に答える
0

これを行うライブラリも知りません。文字列に一致するようにネストされた再帰関数と正規表現(多く)を組み合わせて、日付になるかどうかを判断するための最良の推測を考え出すことをお勧めします。日付はさまざまな方法で書くことができます。「2010年10月3日日曜日」または「2010年10月3日日曜日」または「2010年10月3日」または「2010年10月3日」と書き出す人もいます。さまざまな方法がたくさんあります(他の言語/文化での日付を検討している場合はさらに多くなります)。

于 2010-10-03T17:50:15.283 に答える
0

文字列に「/」文字が2つあるかどうかをいつでも確認できます。

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

他のすべてが整数であるかどうかを確認するために、同様のことを行うことができます。

于 2010-10-03T17:52:52.890 に答える