Bonjour!
Imaginons que j'ai un texte :
"le 12 Avril 1985 le joueur de foot marqua un...
le 22 Juin 1993 il remarque
le 12 avril 1985 c'est le premier film..."
Je veux récupérer les dates en java ainsi:
--> le 12 avril 1985 : compté 2 fois
--> le 22 Juin 1993 compté 1 fois
Comment structureriez vous votre code en java ?
merci
J'utiliserais une Regular Expression, j'imagine ![]()
1 à 2 chiffres + 3 à 9 lettres + 4 chiffres
merci de ta réponse ![]()
J'ai créer une classe RegEx :
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.regex.Pattern;
public class RegEx {
public static boolean isValid(String text) {
if (text == null || !text.matches("\\d{4}-[01]\\d-[0-3]\\d")) {
return false;
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
format.setLenient(false);
try {
format.parse(text);
return true;
} catch (ParseException ex) {
return false;
}
}
}
Je pense que ca doit matcher les dates convenablement. Quelle serait la 2ème étape ?
Merci ![]()
J'utiliserai un DateFormat en spécifiant la locale FR.
Soit : new SimpleDateFormat("dd MMMM yyyy", Locale.FRANCE)
C'est déjà géré par Java. Ce type de problème est tellement complexe (la gestion des dates) qu'il ne vaut mieux pas réinventer la roue ![]()
J'ai rajouté le locale.france, cependant est ce que mon code au dessus est correct ?
Si oui, je pense qu'il faut que je créer une seconde classe.
Une Map avec la date en clef et le nombre d'occurence en valeur répondra à ton besoin.
Je ne vois que 2 parcours (premier : date à parser ; second : affichage) à réaliser, chacun avec très peu de code.
Je ne peux pas répondre sur l'archi, vu que cela dépend de ton application et de la réutilisation probable de ces morceaux de code.
Je varie de "aucune classe, aucune méthode" à "aucune classe, une méthode".
Note : ton code ne fait que vérifier le format de la String passée en paramètre. Tu ignores sa valeur. Cela rentre en conflit avec ce que tu expliques vouloir.
La map a du sens selon moi
merci Bunyan !
Pour l'architecture j'ai opté pour une seule classe pour le moment (RegEx) et une autre qui joue le rôle de main.
Peux tu détailler les 2 parcours que tu me dis plus haut stp ?
Si je comprends bien tu cherches ds ton premier parcours les dates à parser (mais comment si ma fonction isValid(String) ne le fait pas ? ), puis ds le 2eme parcours je pourrai par exemple utiliser un iterator pour boucler dans ma Map de facon à ressortir les valeurs ?
Si tu pouvais m'apporter du sens à savoir comment checker la valeur et non la conformité au format. (ta dernière phrase).
Je te remercie d'avance, c'est vraiment sympa
Petit update !
J'ai continué mon code :
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class RegEx {
Map<String, Integer> mapDates = new HashMap<>(); //String --> 12/01/2012 // Integer --> 2 fois (occurence)
String[] tableauMois = new String[12]; //contient les 12 mois
int counter =1;
//Use of Regular Exprs to determine the different format taken into account in a text
public boolean isValid(String textToValidate) {
if (textToValidate == null || !textToValidate.matches("([0-9]{2})/([0-9]{2})/([0-9]{4})")) { //if text null or doesn't matchs format --> return false
return false;
}
if (textToValidate.matches("([a-z][A-Z]*) ([0-9]{2}) ([0-9]{4})")) { //for --> 'May 15, 2014'
DateFormat formatter;
formatter = new SimpleDateFormat("mm dd, yyyy");
}
if (textToValidate.matches("([0-9]{2})/([0-9]{2})/([0-9]{4})")) { //--> for --> 12/10/1992
if (mapDates.containsKey(textToValidate)) {
counter++;
}
mapDates.put(textToValidate, counter);
DateFormat formatter;
formatter = new SimpleDateFormat("dd/MM/yyyy");
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.FRANCE);
format.setLenient(false); //set to false --> strictly matches my pattern
try {
format.parse(textToValidate);
return true;
} catch (ParseException ex) {
return false;
}
}
}
Le problème est que maintenant (je veux tester le counter) si je rentre en paramètre un texte comme : static String text = "12/12/1996 j'écris puis 12/12/1996";
alors je passerai directement dans mon premier if qui dit que mon bout de texte n'est pas conforme au format.. (je voulais montrer que si on a 2 fois 12/12/1996 la valeur de la map (counter) devrait être augmentée)
Bon, alors, si je comprends bien ton code, si je supprime les parties inutiles, il est équivalent à ceci :
public boolean isValid(String textToValidate) {
return false;
}
Pourquoi ?
Car ton premier if fais un test sur le pattern XX/YY/ZZZZ (où X, Y, et Z sont des chiffres de 0 à 9). Si tu n'as pas ce format de String, tu sors de ta boucle.
Tu passes donc obligatoirement dans ton second "if", qui vérifie si tu as déjà rencontré la String fournie en paramètre et incrémente son compteur si c'est le cas (ce qui est déjà problématique, puisque ton code validerait donc 99/99/9999, qui n'est pas une date valide).
Ensuite, tu crées un dateformatter qui suit le pattern ZZZZ-YY-XX puis tente de parser. Puisque arrivé ici, la String contient obligatoirement un autre masque, le parse ne fonctionne pas, il lève donc une exception, et return "false".
As-tu testé ton code ?
De plus, je ne comprends plus du tout le sens de ta première question puisque tu n'utilises pas le cas "mois complet", son masque étant "MMMM".
Une méthode faite à l'arrache qui correspondrai à ce que tu souhaites :
public Map<Date, Integer> parseDates(List<String> toParse) {
Map<Date, Integer> dates = new HashMap<>();
SimpleDateFormat format = new SimpleDateFormat("dd MMMM yyyy", Locale.FRANCE);
for (String s : toParse) {
try {
Date d = format.parse(s);
if (dates.contain(d)) {
dates.put(d, dates.get(d)+1);
} else {
dates.put(d, 1);
}
} catch (ParseException e) {
// do nothing or do a log, date to parse is not a valid one for us.
}
}
return dates;
}
La seconde boucle dont j'ai parlé qui manquerait serait son print.
Comme tu le notes, je n'utilise aucune expression régulière et laisse le DateFormatter faire ce travail là via son mécanisme d'exception.
Super, merci Bunyan. Après avoir pris compte de tes remarques voici ce que j'ai fait :
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class RegEx {
public Map<Date, Integer> regEx(List<String> toParse) {
Map<Date, Integer> mapDates = new HashMap<>(); //map storing --> key for dates & value for occurrence
//SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy", Locale.FRANCE);
//SimpleDateFormat format1 = new SimpleDateFormat("MMMMM dd, yyyy", Locale.ENGLISH);
for (String s : toParse) {
if (isValid(s)) {
try {
tryParse(s);
//Date currentDateFormated = format.parse(s); //pr chaque string tu le convertis en date --> Allows us to get a formatted date
//Date currentDateFormated1 = format1.parse(s); //on doit retourner un objet Date
if (mapDates.containsKey(tryParse(s))) { //--------> ca veut dire qu'une exception est lancé depuis le if ou le else
mapDates.put(tryParse(s), mapDates.get(tryParse(s)) + 1);
} else {
mapDates.put(tryParse(s), 1);
}
//CATCH ENLEVÉ !
}
catch (Exception e) {
e.printStackTrace();
// do nothing --> date to parse not recognised
}
}
}
toString(mapDates);
return mapDates;
}
public boolean isValid(String textToValidate) { //Reg Ex to validate the correct format (see exemples)
if (textToValidate.matches("([A-Z]*[a-z]*) ([0-9]{2}[,]) ([0-9]{4})")) { //for --> 'May 15, 2014'
return true;
}
if (textToValidate.matches("([0-9]{2})/([0-9]{2})/([0-9]{4})")) { //--> for --> 12/10/1992
return true;
}
return false;
}
public void toString(Map<Date, Integer> datesToString) {
for (Map.Entry<Date, Integer> entry : datesToString.entrySet()) {
System.out.println(entry.getKey() + " number of occurency --> " + entry.getValue());
}
}
public Date tryParse(String dateString) {
List<SimpleDateFormat> possiblePatterns = new ArrayList<SimpleDateFormat>();
possiblePatterns.add(new SimpleDateFormat("dd/MM/yyyy", Locale.FRANCE));
possiblePatterns.add(new SimpleDateFormat("MMMMM dd, yyyy", Locale.ENGLISH));
for (SimpleDateFormat pattern : possiblePatterns) {
try {
return new SimpleDateFormat(pattern.toPattern()).parse(dateString); ////////// to pattern()??!
}
catch (ParseException e) {
}
}
return null;
}
}
J'ai créer une méthode tryParse(String) qui permettra de recouvrir plusieurs cas possibles de formats (ici "'May 15, 2014'" et "12/10/1992").
Cependant si je teste les 2 cas :
toParse.add("12/10/2013");
toParse.add("12/10/2013");
toParse.add("May 15, 2014");l'output est :
null number of occurency --> 1 //on ne veut pas null
Sat Oct 12 00:00:00 CEST 2013 number of occurency --> 2
erreur corrigé j'ai juste eu à ajouter "getTime()". J'ai également modifié le return par un Date et non un SimpleDateFormat.
Imaginons cette fois que j'ai : "bla bla bla , le 12/10/2007" alors là mon Parser ne pourra traiter cette String.
Existe il une solution à cela ?
Oui : faire un pré-traitement de ta chaîne pour extraire la partie date. C'est dans ce cas que tes regex auront enfin une utilité ^^
Update : J'ai réussie à faire fonctionner mon parser correctement !
Cependant j'ai un petit bug dans mes : formatters.add(DateTimeFormatter.ofPattern("MMM dd, y", java.util.Locale.ENGLISH));
--> J'ai l'impression qu'il y ait que le mois de Mai qui fonctionne correctement...(par ex ne fonctionne pas pour 'October')
--> par ex :
"May 10, 2011", "October 20, 2004" seulement le premier fonctionne : le 2ème j'ai un : java.time.format.DateTimeParseException
Une idée ? merci