Reconnaître une chaîne de date arbitraire

Je dois être capable de reconnaître les chaînes de date. Peu importe si Je ne peux pas distinguer entre le mois et la date (par exemple 12/12/10), j'ai juste besoin de classer la chaîne comme étant une date, plutôt que de la convertir en un objet Date. Donc, c'est vraiment un problème de classification plutôt que d'analyse.

J'aurai des morceaux de texte tels que:

"bla bla bla bla 12 Jan 09 bla bla bla 01/04/10 bla bla bla"

Et je dois être capable de reconnaître la limite de début et de fin pour chaque chaîne de date à l'intérieur.

Je me demandais si quelqu'un connaissait des bibliothèques java qui peut faire cela. Mon google-fu n'a rien trouvé jusqu'à présent.

UPDATE: je dois être capable de reconnaître l'ensemble le plus large possible de façons de représenter une date. Bien sûr, la solution naïve pourrait être d'écrire une instruction if pour chaque format concevable, mais une approche de reconnaissance de formes , avec un modèle formé, est idéalement ce que je suis après.

22
demandé sur Joel 2010-10-03 21:26:27

14 réponses

Utiliser JChronic

Vous pouvez utiliser DateParser2 de l'edu.mit.large.génome.package utils.

5
répondu Puspendu Banerjee 2012-04-18 21:04:43

Vous pouvez boucler tous les formats de date Disponibles en 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
        }
    }
}

Cela ne tiendra cependant pas compte des formats de date personnalisés.

5
répondu Bozho 2010-10-19 13:25:16

Règles qui pourraient vous aider dans votre quête:

  1. créez ou trouvez une sorte de base de données avec des mots connus qui correspondent à des mois. Noms abrégés et complets, comme Jan ou January. Lors de la recherche, il doit être insensible à la casse, car février est aussi un mois, bien que la personne qui le tape ait dû être ivre. Si vous envisagez de rechercher des mois non-anglais, une base de données est également nécessaire, car aucune heuristique ne découvrira que "Wrzesień" est Polonais pour septembre.
  2. Pour l'anglais seulement, consultez nombres ordinaux {[17] } et créez également une base de données pour les nombres 1 à 31. Ceux-ci seront utiles pendant des jours et des mois. Si vous voulez utiliser cette approche pour d'autres langues, alors vous devrez faire vos propres recherches.
  3. encore une fois, en anglais seulement, Vérifiez "Anno Domini" et "Before Christ", C'est-à-dire, AD et BC respectivement. Ils peuvent également être en forme A. D. et B. C.
  4. en ce qui concerne les nombres eux-mêmes qui représenteront des jours, des mois et des années, vous devez savoir où votre limite être. Est-ce 0-9999, ou plus? Autrement dit, Voulez-vous rechercher des dates qui représentent des années au-delà de l'année 9999? Si non, alors les chaînes qui ont 1-4 chiffres consécutifs sont de bonnes suppositions pour un jour, un mois ou une année valide.
  5. Les Jours et les mois ont un ou deux chiffres. Les zéros en tête sont acceptables, donc les chaînes avec un format de 0*, Où * peut être 1-9 sont acceptables.
  6. Les séparateurs peuvent être difficiles, mais si vous n'autorisez pas le formatage incohérent comme 10/20 \ 1999, alors vous vous économiserez beaucoup de chagrin. C'est parce que 10*20*1999 peut-être une date valide, avec * étant généralement un élément de l'ensemble {-,_, ,:,/,\,.,','}, mais il est possible que * est une combinaison de 2 ou 3 éléments mentionnés ensemble. Encore une fois, vous devez choisir des séparateurs acceptables. 10?20?1999 peut être une date valide pour quelqu'un avec un sens étrange de l'élégance. 10 / 20 / 1999 peut aussi être une date valide, mais 10_/20_/1999 serait très étrange.
  7. Il y a des cas sans séparateur. Par exemple: 10Jan1988. Ces cas utilisent des mots de 1.
  8. Il y a des cas particuliers, comme le 28 ou le 29 février, selon l'année bissextile. En outre, des mois avec 30 ou 31 jours.

Je pense que ceux-ci sont suffisants pour une classification "naïve", un expert linguiste pourrait vous aider davantage.

Maintenant, une idée pour votre algorithme. La vitesse n'a pas d'importance. Il peut y avoir plusieurs passages sur la même chaîne. Optimiser quand il commence à la matière. Lorsque vous doutez que vous avez trouvé une chaîne de date, stockez-la quelque part "en sécurité" dans un ListOfPossibleDates et faites un examen une fois de plus, avec des règles plus rigides en utilisant des combinaisons de 1. 8. Lorsque vous pensez qu'une chaîne de date est valide, transmettez-la à la classe Date pour voir si elle est vraiment valide. Le 32 mars 1999 n'est pas valide, lorsque vous le convertissez dans un format que Date comprendra.

Un modèle récurrent important est lookbehind et lookaround. Lorsque vous croyez qu'une entité valide (jour, mois, année) est trouvée, vous devrez voir ce qui se cache derrière et après. Un mécanisme basé sur une pile ou une récursivité peut aider ici.

Étapes:

  1. recherchez dans votre chaîne les mots de la règle 1. Si vous en trouvez un, Notez cet emplacement. Remarque le mois. Maintenant, allez quelques personnages derrière et quelques devant pour voir ce qui vous attend. S'il n'y a pas d'espaces avant et après votre mois, et qu'il y a des nombres, comme dans la règle 7., vérifier leur validité. Si l'un d'eux représente un jour (doit être 0-31) et l'autre une année (doit être 0-9999, éventuellement avec AD ou BC), vous avez un candidat. Si il y a le même séparateurs avant et après, recherchez les règles de 6. Rappelez-vous toujours que vous devez être sûr qu'une combinaison valide existe. donc, 32Jan1999 ne fera pas.
  2. recherchez dans votre chaîne d'autres mots anglais, à partir des règles 2. et 3. Répétez la même chose comme à l'étape 1.
  3. recherche de séparateurs. L'espace vide qui sera la plus délicate. Essayez de les trouver par paires. Donc, si vous avez un "/" dans votre chaîne, trouvez-en un autre et voyez ce qu'ils ont entre eux. Si vous trouvez une combinaison de séparateurs, à la même chose chose. Utilisez également l'algorithme de l'étape 2.
  4. Recherche de chiffres. Les valides sont 0-9999 avec des zéros en tête autorisés. Si vous en trouvez un, Recherchez des séparateurs comme à l'étape 3.

Comme il y a littéralement un nombre incalculable de possibilités, vous ne serez pas en mesure de les attraper tous. Une fois que vous avez trouvé un motif qui, selon vous, pourrait se produire à nouveau, stockez-le quelque part et vous pouvez l'utiliser comme une expression rationnelle pour passer d'autres chaînes.

Prenons votre exemple, "bla bla bla bla 12 Jan 09 bla bla bla 01/04/10 bla bla bla". Après vous extrayez la première date, 12 Jan 09, puis utilisez le reste de cette chaîne ("bla bla bla 01/04/10 bla bla bla") et appliquez à nouveau toutes les étapes ci-dessus. De cette façon, vous serez sûr de ne rien manquer.

J'espère que ces suggestions seront au moins de l'aide. Si il n'existe pas de bibliothèque pour faire toutes ces sales (et plus) étapes pour vous, alors vous avez une route difficile devant vous. Bonne chance!

5
répondu darioo 2010-11-11 08:51:14

Je l'ai fait avec une énorme regex (self created):

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

Cela correspond aux dates suivantes:

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

Et pas ceci:

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

, Il correspond également à des dates entre les symboles comme [],(), ,:

Yesterday (6 nov 2010)

Il correspond à des dates Sans année:

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

Mais elle correspond à:

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

Et ça ne ressemble plus à un rendez-vous. Mais c'est quelque chose que vous pouvez résoudre en vérifiant si les nombres sont des valeurs possibles pour un mois, jour, année.

4
répondu Martijn Courteaux 2010-11-07 14:30:47

Très bon jour de l'analyseur en java est Natty, vous pouvez essayer de ici

3
répondu Matt 2013-07-17 12:52:51

Je suis sûr que les chercheurs de information extraction ont examiné ce problème, mais je n'ai pas trouvé de document.

Une chose que vous pouvez essayer est de le faire en deux étapes. (1) après avoir recueilli autant de données que vous le pouvez, extrayez des fonctionnalités, certaines fonctionnalités qui vous viennent à l'esprit: nombre de nombres qui apparaissent dans la chaîne, Nombre de nombres de 1-31 qui apparaissent dans la chaîne, Nombre de nombres de 1-12 qui apparaissent dans la chaîne, Nombre de noms de mois qui apparaissent dans la chaîne, sur. (2) Apprendre des fonctionnalités en utilisant un type de méthode de classification binaire (SVM par exemple) et enfin (3) lorsqu'une nouvelle chaîne arrive, extraire les fonctionnalités et interroger le SVM pour une prédiction.

2
répondu carlosdc 2010-10-17 08:27:52

Voici un exemple simple de natty:

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
2
répondu MD. Mohiuddin Ahmed 2013-12-09 05:19:16

Peut-être que vous devriez utiliser des expressions régulières?

Espérons que celui-ci fonctionnerait pour le format MM-JJ-AAAA:

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

Ici (0[1-9]|1[012]) correspond au mois 00..12, (0[1-9]|[12][0-9]|3[01]) correspond à une date 00..31 et (19|20)\d\d correspond à une année.

Les champs peuvent être supprimés par un tiret, une barre oblique ou un point.

Cordialement, Serge

1
répondu zserge 2010-10-03 17:46:21

Il est pratiquement impossible de reconnaître tous les formats de date possibles comme des dates en utilisant des algorithmes "standard". C'est juste parce qu'il ya beaucoup d'entre eux.

Nous, les humains, sommes capables de le faire juste parce que nous avons appris que quelque chose comme 2010-03-31 ressemble à la date. En d'autres termes, je suggère d'utiliser des algorithmes D'apprentissage automatique et d'apprendre à votre programme à reconnaître des séquences de dates valides. Avec Google Prediction API cela devrait être faisable.

Ou vous pouvez utiliser les Expressions comme suggéré ci-dessus, pour détecter certains mais pas tous les formats de date.

1
répondu Paweł Dyda 2010-10-04 11:52:18

Ce que je ferais est de chercher des caractéristiques de date, plutôt que les dates elles-mêmes. Par exemple, vous pouvez rechercher des barres obliques, (pour obtenir les dates du formulaire 1/1/1001), des tirets (1 - 1 - 1001), noms et abréviations des mois (1er janvier 1001 ou 1er janvier 1001). Lorsque vous obtenez un coup pour ceux-ci, collectez les mots à proximité (2 de chaque côté devrait être bien) et stockez-le dans un tableau de chaînes. Une fois que vous avez scanné toutes les entrées, vérifiez ce tableau de chaînes avec une fonction qui ira un peu plus en profondeur et tirera sur les chaînes de date réelles, en utilisant les méthodes trouvées ici. La chose importante est juste obtenir les dates générales à un niveau gérable.

1
répondu David Watson 2010-11-10 08:28:51

Habituellement, les dates sont des caractères séparés par une barre oblique ou un tiret. Avez-vous envisagé une expression régulière?

Je suppose que vous ne cherchez pas à classer les dates du type dimanche, 3 octobre 2010 et ainsi de suite

0
répondu npinti 2010-10-03 17:32:40

Je ne connais aucune bibliothèque qui puisse le faire, mais écrire la vôtre ne serait pas incroyablement difficile. En supposant que vos dates sont toutes formatées avec les barres obliques comme 12/12/12, Vous pouvez vérifier que vous avez trois ' \ ' s. Vous pouvez obtenir encore plus technique et le faire vérifier les valeurs entre les barres obliques. Par exemple, si vous avez:

30/12/10

, Alors vous savez que 30 jours et 12 mois. Toutefois, si vous obtenez 30/30/10 vous savez que même si ti a le bon format, il ne peut pas être une date car il n'y a pas de " 30 " mois.

0
répondu Glenn Nelson 2010-10-03 17:33:05

Je ne connais aucune bibliothèque qui le fasse non plus. Je suggérerais un mélange de fonctions récursives imbriquées et d'expressions régulières (beaucoup) pour faire correspondre les chaînes et essayer de trouver une meilleure estimation pour voir si cela peut être une date. Les Dates peuvent être écrites de différentes manières, certaines personnes peuvent les écrire comme "dimanche 3 octobre 2010" ou "dimanche 3 octobre 2010" ou "10/03/2010" ou "10/3/2010" et tout un tas de façons différentes (encore plus si vous envisagez des dates dans d'autres langues/cultures).

0
répondu prototypef 2010-10-03 17:50:15

Vous pouvez toujours vérifier s'il y a deux caractères '/' dans une chaîne.

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

Vous pouvez faire quelque chose de similaire pour vérifier si tout le reste est un entier.

0
répondu Salem 2010-10-03 17:52:52