Fechas de análisis de una aplicación OCR -- java campo con parsing campo con datetime campo con i18n campo con localization camp codereview Relacionados El problema

Parsing dates from an OCR application


7
vote

problema

Español

Escribí este código para analizar las fechas de la salida del OCR, lo que significa que la fecha obtenida puede ser literalmente cualquier cosa, por lo que puse algunas restricciones en su lugar:

  • Fecha es el formato de: s6 , donde los campos son de cada día, mes o año y cualquier delimitador se puede usar para dividir los números, esto también se denomina formato corto de fechas.
  • Los campos consisten en solo números. (Así que no hay meses como texto)
  • se conoce el local.

Primero jugué con s7 , pero resultó ser solo de utilidad para formatear, y no para analizar, ya que solo da un formato específico uno por regalo .

Así que decidí desplegar mi propio código al tiempo que tiene la intención de usar la mayor cantidad de características de la biblioteca de Java como sea posible (principalmente de s8 y s9 ).

La clase de prueba:

  strcat0  

la clase del analizador:

  strcat1  

Me gustaría tener una revisión general con un enfoque adicional sobre cómo poder admitir tantas variaciones y lugares de entrada como sea posible.

Este tipo de problema también tiene algunos problemas prácticos que solo descubrirá cuando se practica resolviendo este problema, por lo que lo aliento encarecidamente que revise este código con un IDE en la mano para poder probar sugerencias alternativas.

Original en ingles

I wrote this code to parse dates from the output of the OCR, which means that the obtained date can be literally anything, so I put some restrictions in place:

  • Date is the the format of: field1?field2?field3, where the fields are either day, month or year and any delimiter can be used to split the numbers, this is also called the short format of dates.
  • The fields consist of only numbers. (So no months as text)
  • The locale is known.

I first played around with DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale), but it turned out to be only of use for formatting, and not for parsing, as it only gives one specific format per locale.

So I decided to roll out my own code whilst still intending to use as many Java library features as possible (mainly from java.util.Locale and java.time).

The test class:

public class DateParserTest {     @Test     public void testParseDutchDate() {         List<String> dates = Arrays.asList(             "02-10-2014",             "2-10-2014",             "02-10-14",             "2-10-14",             "02/10/2014",             "02 10 2014"         );          for (String date : dates) {             Locale locale = new Locale("nl");              LocalDate localDate = DateParser.parseShortDate(date, locale);              assertEquals(LocalDate.of(2014, 10, 2), localDate);         }     }      @Test     public void testParseAmericanDate() {         List<String> dates = Arrays.asList(             "10-02-2014",             "10-2-2014",             "10-02-14",             "10-2-14",             "10/02/2014",             "10 02 2014"         );          for (String date : dates) {             Locale locale = new Locale("en-US");              LocalDate localDate = DateParser.parseShortDate(date, locale);              assertEquals(LocalDate.of(2014, 10, 2), localDate);         }     } } 

The parser class:

public final class DateParser {     private DateParser() {         throw new UnsupportedOperationException();     }      private final static Pattern PARSE_DATE_PATTERN = Pattern.compile("(?iuU)^\\W*([\\w]+)\\W+([\\w]+)\\W+([\\w]+)\\W*$");      private static final Pattern DATE_PATTERN_EXTRACTION_PATTERN = Pattern.compile("^(\\w+)\\W+(\\w+)\\W+(\\w+)$");      public static LocalDate parseShortDate(final String text, final Locale locale) {         Objects.requireNonNull(text, "text");         Objects.requireNonNull(locale, "locale");          Matcher matcher = PARSE_DATE_PATTERN.matcher(text);         if (!matcher.matches()) {             throw new IllegalStateException("Date " + text + " for locale " + locale + " could not be matched");         }          String match1 = matcher.group(1);         String match2 = matcher.group(2);         String match3 = matcher.group(3);          String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, null, Chronology.ofLocale(locale), locale);          Matcher datePatternMatcher = DATE_PATTERN_EXTRACTION_PATTERN.matcher(pattern);         if (!datePatternMatcher.matches()) {             throw new IllegalStateException("Date format " + pattern + " for locale " + locale + " could not be processed");         }         String datePatternMatch1 = datePatternMatcher.group(1);         String datePatternMatch2 = datePatternMatcher.group(2);         String datePatternMatch3 = datePatternMatcher.group(3);          String resolvedPattern = new StringBuilder()             .append(String.join("", Collections.nCopies(match1.length(), datePatternMatch1.substring(0, 1))))             .append('-')             .append(String.join("", Collections.nCopies(match2.length(), datePatternMatch2.substring(0, 1))))             .append('-')             .append(String.join("", Collections.nCopies(match3.length(), datePatternMatch3.substring(0, 1))))             .toString();          return LocalDate.parse(String.join("-", match1, match2, match3), DateTimeFormatter.ofPattern(resolvedPattern, locale));     } } 

I'd like to have a general review with extra focus on how to be able to support as many input variations and locales as possible.

This type of problem also has some practical issues you will only find out when practicing by solving this problem, so I strongly encourage you to review this code with an IDE at hand to be able to test alternative suggestions.

              

Lista de respuestas

2
 
vote

Le recomendaría agregar en una prueba para verificar todo el 99887766655544330 en la plataforma, si puede analizar una fecha formateada con esa localidad.

La prueba correspondiente sería:

  @Test public void testAllLocales() {     LocalDate specificLocalDate = LocalDate.of(2014, 10, 2);      Locale[] locales = Locale.getAvailableLocales();     for (Locale locale : locales) {         DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale);         String date = specificLocalDate.format(dateTimeFormatter);          LocalDate localDate = DateParser.parseShortDate(date, locale);          assertEquals("for " + date + " using " + locale, specificLocalDate, localDate);     } }   

Uso de esto Encontramos algunos bichos astutos:

  1. No funciona para el local hr_HR , ya que usa el formato FECHA dd.MM.yy , que tiene un punto al final. Para solucionar esto, debemos cambiar el DATE_PATTERN_EXTRACTION_PATTERN a Pattern.compile("^\W*(\w+)\W+(\w+)\W+(\w+)\W*$") . Tenga en cuenta que ahora permitimos que los números coinciden de las no palabras al principio y el final del formato de fecha.
  2. El DATE_PATTERN_EXTRACTION_PATTERN debe funcionar con Unicode, ya que los idiomas posiblemente pueden usar caracteres Unicode como separadores, por lo que debe ser: Pattern.compile("(?iuU)^\W*(\w+)\W+(\w+)\W+(\w+)\W*$") .
  3. Hay un error usando el local zh_HK , supuestamente debido a los caracteres de Unicode en la fecha formateada, ya que no puede extraer los tres números de la fecha.
  4. El ja_JP_JP_#u-ca-japanese9 La configuración regional no funciona, pero este puede ser debido a un error JDK: https://stackoverflow.com/questions/26169008/ JA-JP-JP-U-CA-JAPONSUESTRY-REFORE-NOT-SER-SER REEMBOLSADO POR-SUPERIOR-PATTEL-PATTEL-TOWS-T
 

I would recommend you to add in a test to check for every available Locale on the platform, if you can parse a date formatted using that locale.

The corresponding test would be:

@Test public void testAllLocales() {     LocalDate specificLocalDate = LocalDate.of(2014, 10, 2);      Locale[] locales = Locale.getAvailableLocales();     for (Locale locale : locales) {         DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale);         String date = specificLocalDate.format(dateTimeFormatter);          LocalDate localDate = DateParser.parseShortDate(date, locale);          assertEquals("for " + date + " using " + locale, specificLocalDate, localDate);     } } 

Using this we find a few sneaky bugs:

  1. It does not work for the locale hr_HR, as it uses date format dd.MM.yy, which has a dot at the end. To fix this we need to change the DATE_PATTERN_EXTRACTION_PATTERN to Pattern.compile("^\\W*(\\w+)\\W+(\\w+)\\W+(\\w+)\\W*$"). Note that we now allow any number matches of non-words at the start and end of the date format.
  2. The DATE_PATTERN_EXTRACTION_PATTERN should work using Unicode, as languages may possibly use Unicode characters as separators, so it should be: Pattern.compile("(?iuU)^\\W*(\\w+)\\W+(\\w+)\\W+(\\w+)\\W*$").
  3. There is bug using the locale zh_HK, supposedly due to unicode characters in the formatted date, as it is not able to extract the three numbers from the date.
  4. The ja_JP_JP_#u-ca-japanese locale does not work, but this may be due to a JDK bug: https://stackoverflow.com/questions/26169008/the-ja-jp-jp-u-ca-japanese-locale-cannot-be-reparsed-by-its-own-pattern-using-t
 
 
1
 
vote

Tengo que publicar esto como una respuesta, ya que no puedo hacer comentarios todavía:

¿Realmente desea lanzar un @Test public void testAllLocales() { LocalDate specificLocalDate = LocalDate.of(2014, 10, 2); Locale[] locales = Locale.getAvailableLocales(); for (Locale locale : locales) { DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale); String date = specificLocalDate.format(dateTimeFormatter); LocalDate localDate = DateParser.parseShortDate(date, locale); assertEquals("for " + date + " using " + locale, specificLocalDate, localDate); } } 0 si no puede extraer la fecha? Implica que si su método se llamó con algo que no puede analizar o una cadena vacía, eso sería un error de programación. Que, dependiendo de cómo se use su código, puede o no ser correcto.

También como una nota lateral, espero que no esté captando ese @Test public void testAllLocales() { LocalDate specificLocalDate = LocalDate.of(2014, 10, 2); Locale[] locales = Locale.getAvailableLocales(); for (Locale locale : locales) { DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale); String date = specificLocalDate.format(dateTimeFormatter); LocalDate localDate = DateParser.parseShortDate(date, locale); assertEquals("for " + date + " using " + locale, specificLocalDate, localDate); } } 1 en el código de llamada.

 

I have to post this as an answer, as I cannot comment yet:

Do you really want to throw an IllegalStateException if you cannot extract the date? It implies that if your method ever got called with something you cannot parse or an empty string, that would be a programming error. Which, depending on how your code is used, may or may not be correct.

Also as a side note, I hope you're not catching that IllegalStateException in the calling code.

 
 
 
 
0
 
vote

Esto podría resolverse con org.joda.time.Format.DateTimeFormat.

En cuanto a las pruebas. No afirmes en el bucle FOR. Si la afirmación falla, entonces la prueba completa falla. Entonces, si falla en la primera cita, entonces no sabes nada sobre otras fechas. Use las pruebas parametrizadas en su lugar.

 

This could be solved with org.joda.time.format.DateTimeFormat.

As for the tests. Do not assert in the for loop. If the assertion fails then the whole test fails. So if it fails on the first date, then you don't know anything about other dates. Use parameterized tests instead.

 
 

Relacionados problema

4  Métodos de extensión para el motor de traducción  ( Extension methods for translation engine ) 
Nuestra aplicación utiliza algunos singletos para cadenas localizadas. Estas llamadas solían ser largas y sucias. Por lo tanto, algún compañero de trabajo cre...

4  Cargador de localidad simple  ( Simple locale loader ) 
Tengo una clase que carga local de una base de datos. No estoy seguro de si el local es la palabra correcta. Básicamente, está cargando textos para un juego, ...

3  Aplicación multilingüe sin localización  ( Multilingual app without localizing ) 
Estoy haciendo una aplicación de iOS que admite múltiples idiomas, pero no en la forma localizada incorporada. En iOS, para que la localización funcione, el u...

5  Localización WPF - Uso de Resex  ( Wpf localisation using resx ) 
Tengo una pequeña aplicación WPF que no estoy realizando exactamente, pero tampoco codifican recursos. Sé que WPF no usa archivos .resx para ese propósito, pe...

4  Ideas del sistema de error bilingüe PHP  ( Php bilingual error system ideas ) 
Estoy construyendo un sitio web móvil basado en PHP que debe estar en español e inglés. Este es mi primer sitio bilingüe, por lo que he estado teniendo que vo...

8  Traducir documentos de látex en diferentes idiomas con DEEPL API  ( Translating latex documents into different languages using deepl api ) 
Vengo de C y C ++ y que a menudo tiene problemas con el máximo potencial de Python. Este es un script que escribí para traducir documentos de látex en diferen...

3  Incluyendo recursos de recursos en Enum para mostrar el nombre constante enum en diferentes idiomas [cerrado]  ( Including resourcebundle in enum in order to display enum constant name in diffe ) 
cerrado. Esta pregunta es off-topic . Actualmente no está aceptando respuestas. ¿Quieres ...

4  ¿Trabajar con locales en swing (con cambio en tiempo de ejecución)?  ( Working with locales in swing with change at runtime ) 
Pensé en la mejor manera de implementar la localización con tiempo de ejecución en el swing. Actualmente resuelvo el problema como ese: JMenu menuData = ...

5  Algoritmos de cadena y locale  ( String algorithms and locale ) 
Estoy tratando de escribir algunos algoritmos de cadena que quiero trabajar para cualquier tipo de cadenas y / o local. Logré obtener algunos resultados que t...

0  JavaScript - Diferencia entre dos fechas en días o menos [cerrado]  ( Javascript difference between two dates in days or less ) 
cerrado. Esta pregunta es off-topic . Actualmente no está aceptando respuestas. ¿Quieres ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos