algorithm text string wrapping word-wrap

algorithm - Algoritmo para volver a envolver texto envuelto?



string wrapping (3)

Digamos que he escrito una aplicación de administración de correo electrónico personalizada para la empresa para la que trabajo. Lee los correos electrónicos de la cuenta de soporte de la empresa y almacena versiones limpias y en texto sin formato de ellos en una base de datos, y hace otras cosas interesantes como asociarlas con cuentas de clientes y pedidos en el proceso. Cuando un empleado responde a un mensaje, mi programa genera un correo electrónico que se envía al cliente con una versión formateada del hilo de discusión. Si el cliente responde, la aplicación busca un número único en la línea de asunto para leer el mensaje entrante, eliminar la discusión anterior y agregarlo como un nuevo elemento en el hilo. Por ejemplo:

This is a message from Contoso customer service. Recently, you requested customer support. Below is a summary of your request and our reply. -------------------------------------------------------------------- Contoso (Fred) on Tuesday, December 30, 2008 at 9:04 a.m. -------------------------------------------------------------------- John: I''ve modified your address. You can confirm my work by logging into "Your Account" on our Web site. Your order should ship out today. Thanks for shopping at Contoso. -------------------------------------------------------------------- You on Tuesday, December 30, 2008 at 8:03 a.m. -------------------------------------------------------------------- Oops, I entered my address incorrectly. Can you change it to Fred Smith 123 Main St Anytown, VA 12345 Thanks! -- Fred Smith Contoso Product Lover

En general, todo esto funciona muy bien, pero hay un área que de alguna manera pospuse la limpieza desde hace un tiempo, y trata sobre el ajuste del texto. Para generar el bonito formato de correo electrónico como el anterior, necesito volver a ajustar el texto que el cliente envió originalmente.

He escrito un algoritmo que hace esto (aunque al mirar el código, no estoy del todo seguro de cómo funciona, podría usar alguna refactorización). Pero no puede distinguir entre una nueva línea de envolvente dura, una nueva línea de "fin de párrafo" y una nueva línea "semántica". Por ejemplo, una nueva línea dura es aquella que el cliente de correo electrónico insertó dentro de un párrafo para envolver una larga línea de texto, digamos, en 79 columnas. Una nueva línea de fin de párrafo es aquella que el usuario agregó después de la última oración de un párrafo. Y una línea nueva semántica sería algo así como la etiqueta br , como la dirección que el Fred tipeó arriba.

En mi lugar, mi algoritmo solo ve dos líneas nuevas en una fila como indicando un nuevo párrafo, por lo que el formato del correo electrónico del cliente sería similar al siguiente:

Oops, I entered my address incorrectly. Can you change it to Fred Smith 123 Main St Anytown, VA 12345 Thanks! -- Fred Smith Contoso Product Lover

Cada vez que trato de escribir una versión que envuelva de nuevo este texto como estaba previsto, básicamente choco contra una pared en el sentido de que necesito conocer la semántica del texto, la diferencia entre una nueva línea "dura" y una "realmente quise decir" es como una nueva línea tipo br , como en la dirección del cliente. (Utilizo dos nuevas líneas seguidas para determinar cuándo comenzar un nuevo párrafo, que coincide con la forma en que la mayoría de las personas realmente escriben correos electrónicos).

¿Alguien tiene un algoritmo que puede volver a envolver el texto como estaba previsto? ¿O esta implementación es "lo suficientemente buena" al sopesar la complejidad de una solución dada?

Gracias.


Podría intentar verificar si se ha insertado una línea nueva para mantener la longitud de la línea por debajo de un máximo (también conocido como envoltura rígida): simplemente verifique la línea más larga del texto. Luego, para cualquier línea dada, le agrega la primera palabra de la siguiente línea. Si la línea resultante excede la longitud máxima, el salto de línea probablemente fue una envoltura difícil.

Aún más simple, podría considerar todas las rupturas en (maxlength - 15) <= length <= maxlength como hardwraps (siendo 15 una suposición educada). Esto sin duda filtrará las interrupciones intencionales, como las direcciones y otras cosas, y cualquier interrupción omitida en este rango no influiría demasiado en el resultado.


Siguiendo los consejos anteriores de Ole, volví a trabajar en mi implementación para ver un umbral. Parece manejar la mayoría de los escenarios que arrojo lo suficientemente bien sin que me tenga que volver loco y escribir código que realmente entienda el idioma inglés.

Básicamente, primero escaneo a través de la cadena de entrada y grabo la longitud de línea más larga en la variable inputMaxLineLength . Luego, cuando estoy reencaminando, si encuentro una nueva línea que tiene un índice entre inputMaxLineLength y 85% de inputMaxLineLength , entonces reemplazo esa nueva línea con un espacio porque creo que es una nueva línea dura, a menos que sea seguida inmediatamente por otra nueva línea, porque entonces supongo que es solo un párrafo de una sola línea que sucede dentro de ese rango. Esto puede suceder si alguien escribe una lista corta con viñetas, por ejemplo.

Ciertamente no es perfecto, pero es "lo suficientemente bueno" para mi escenario, ya que el texto suele ser medio destrozado por un cliente anterior de correo electrónico, para empezar.

Aquí hay un código, mi implementación de unas pocas horas de uso que probablemente aún se desarrolle en algunos casos extremos (usando C #). Es mucho menos complicado que mi solución anterior, lo cual es bueno.

Código fuente

Y aquí hay algunas pruebas unitarias que ejercen ese código (usando MSTest):

Código de prueba

Si alguien tiene una mejor implementación (y sin duda existe una mejor implementación), ¡me complacerá leer sus pensamientos! Gracias.


Tengo dos sugerencias, de la siguiente manera.

  • Presta atención a la puntuación: esto te ayudará a distinguir entre una nueva línea "dura" y una línea nueva "final de párrafo" (porque, si la línea termina con un punto, entonces es más probable que el usuario haya pensado que sea un final de párrafo.

  • Preste atención a si una línea es mucho más corta que la longitud máxima de la línea: en el ejemplo anterior, puede tener texto que está siendo "envuelto" en 79 caracteres, además de que tiene líneas de dirección de solo 30 caracteres de longitud; porque 30 es mucho menos que 79, usted sabe que las líneas de direcciones se rompieron por el usuario y no por el algoritmo de ajuste de texto del usuario.

Además, preste atención a las sangrías: se puede suponer que las líneas que tienen sangría con espacios en blanco de la izquierda son nuevos párrafos, separados de las líneas anteriores, como lo están en este foro.