Consejo 112 de Java: Mejore la tokenización de cadenas ricas en información

La mayoría de los programadores de Java han utilizado la java.util.StringTokenizerclase en algún momento u otro. Es una clase útil que básicamente tokeniza (rompe) la cadena de entrada basándose en un separador y proporciona los tokens a pedido. (La tokenización es el acto de convertir secuencias de caracteres en tokens que su programa entiende).

Aunque es útil, StringTokenizersu funcionalidad es limitada. La clase simplemente busca el delimitador en la cadena de entrada y rompe la cadena una vez que se encuentra el delimitador. No comprueba condiciones como si el delimitador está dentro de una subcadena, ni devuelve el token como ""(longitud de cadena 0) una vez que se encuentran dos delimitadores consecutivos en la entrada. Para cumplir con estas limitaciones, la plataforma Java 2 (JDK 1.2 en adelante) viene con la BreakIteratorclase, que es un tokenizador mejorado StringTokenizer. Dado que esta clase no está presente en JDK 1.1.x, los desarrolladores a menudo pasan mucho tiempo escribiendo un tokenizador original que cumpla con sus requisitos. En un proyecto grande que involucra el manejo de formatos de datos, no es raro encontrar muchas clases personalizadas flotando alrededor.

Este consejo tiene como objetivo guiarlo a través de la escritura de un tokenizador sofisticado, utilizando el archivo StringTokenizer.

Limitaciones de StringTokenizer

Puede crear un StringTokenizerutilizando cualquiera de los siguientes tres constructores:

  1. StringTokenizer(String sInput): Saltos en espacios en blanco ( " ", "\t", "\n").
  2. StringTokenizer(String sInput, String sDelimiter): Continúa sDelimiter.
  3. StringTokenizer(String sInput, String sDelimiter, boolean bReturnTokens): Se interrumpe sDelimiter, pero si bReturnTokensse establece en verdadero, el delimitador también se devuelve como un token.

El primer constructor no comprueba si la cadena de entrada contiene subcadenas. Cuando la cadena "hello. Today \"I am \" going to my home town"se tokenized en el espacio blanco, el resultado está en fichas hello., Today, "I, am, ", going, en lugar de hello., Today, "I am ", going.

El segundo constructor no comprueba la aparición consecutiva de delimitadores. Cuando la cadena "book, author, publication,,,date published"está en tokenized ",", las StringTokenizerdeclaraciones de cuatro fichas con valores de book, author, publication, y date publisheden lugar de los seis valores book, author, publication, "", "", y date published, en ""medio cadena de longitud 0. Para obtener seis, debe establecer el StringTokenizer's bReturnTokensparámetro en true.

La característica de establecer el parámetro en verdadero es importante, ya que da una idea de la presencia de delimitadores consecutivos. Por ejemplo, si los datos se obtienen dinámicamente y se utilizan para actualizar una tabla en una base de datos, donde los tokens de entrada se asignan a los valores de las columnas, entonces no podemos asignar los tokens con las columnas de la base de datos, ya que no estamos seguros de qué columnas deben establecerse. a "". Por ejemplo, queremos agregar registros a una tabla con seis columnas y los datos de entrada contienen dos delimitadores consecutivos. El resultado de StringTokenizeren este caso son cinco tokens (ya que dos delimitadores consecutivos representan el token "", que se StringTokenizerignora), y tenemos que establecer seis campos. Tampoco sabemos dónde aparece el delimitador consecutivo, por lo tanto, en qué columna se debe establecer "".

El tercer constructor no funcionará si un token en sí es igual (en longitud y valor) al delimitador y está en una subcadena. Cuando la cadena "book, author, publication,\",\",date published"se tokenized (esta cadena contiene ,como una muestra, que es el mismo que su delimitador) en la cadena ,, el resultado es book, author, publication, ", ", date published(con seis fichas) en lugar de book, author, publication, ,(el carácter coma), date published(con cinco fichas). Eso sí, incluso establecer el bReturnTokens(tercer parámetro en StringTokenizer) en verdadero no lo ayudará en este caso.

Necesidades básicas de un tokenizador

Antes de trabajar con el código, necesitará conocer las necesidades básicas de un buen tokenizador. Dado que los desarrolladores de Java se utilizan para la StringTokenizerclase, una buena tokenizer debe tener todos los métodos útiles que ofrece la clase, tales como hasMoreTokens(), nextToken(), countTokens().

El código de esta sugerencia es simple y en su mayoría se explica por sí mismo. Básicamente, he usado la StringTokenizerclase (creada con bReturnTokensestablecido en verdadero) internamente y proporcioné los métodos mencionados anteriormente. Dado que en algunos casos el delimitador se requiere como tokens (casos muy raros) mientras que en otros no lo es, el tokenizador debe proporcionar el delimitador como un token a pedido. Cuando crea un PowerfulTokenizerobjeto, pasando solo la cadena de entrada y el delimitador, usa internamente un StringTokenizerwith bReturnTokensestablecido en verdadero. (La razón de esto es que si StringTokenizerse crea a sin que se bReturnTokensestablezca en verdadero, entonces está limitado para superar los problemas mencionados anteriormente). Para manejar el tokenizador correctamente, el código verifica si bReturnTokensestá establecido en verdadero en algunos lugares (calculando el número total de tokens y nextToken()).

Como habrás observado, PowerfulTokenizerimplementa la Enumerationinterfaz, implementando así los métodos hasMoreElements()y nextElement()que simplemente delegan la llamada a hasMoreTokens()y nextToken(), respectivamente. (Al implementar la Enumerationinterfaz, se PowerfulTokenizervuelve compatible con versiones anteriores StringTokenizer). Consideremos un ejemplo. Digamos que la cadena de entrada es "hello, Today,,, \"I, am \", going to,,, \"buy, a, book\""y el delimitador es ,. Esta cadena cuando se tokeniza devuelve valores como se muestra en la Tabla 1:

Tabla 1: Valores devueltos por cadena tokenizada
Tipo Numero de tokens Tokens

StringTokenizer

(bReturnTokens = true)

19 hello:,: Today:,:,:,: "I:,: am ":,: going to:,:,:,: "buy:,: a:,: book"(aquí el personaje :separa las fichas)

PowerfulTokenizer

(bReturnTokens = true)

13 hello:,:Today:,:"":"":I, am:,:going to:,:"":"":buy a book(donde ""significa cadena de longitud 0)

PowerfulTokenizer

(bReturnTokens = false)

9 hello:Today:"":"":I am:going to:"":"":buy a book

La cadena de entrada contiene 11 caracteres de coma ( ,), de los cuales tres están dentro de subcadenas y cuatro aparecen consecutivamente (como Today,,,hace dos apariciones de coma consecutivas, la primera coma Todayes el delimitador). Aquí está la lógica para calcular la cantidad de tokens en el PowerfulTokenizercaso:

  1. En el caso de bReturnTokens=true, multiplique el número de delimitadores dentro de las subcadenas por 2 y reste esa cantidad del total real para obtener el recuento de tokens. La razón es que, para la subcadena "buy, a, book", StringTokenizerdevolverá cinco tokens (es decir, buy:,:a:,:book), mientras PowerfulTokenizerque devolverá un token (es decir, buy, a, book). La diferencia es cuatro (es decir, 2 * número de delimitadores dentro de la subcadena). Esta fórmula es válida para cualquier subcadena que contenga delimitadores. Tenga en cuenta el caso especial en el que el token en sí es igual al delimitador; esto no debería disminuir el valor de recuento.
  2. De manera similar, para el caso de bReturnTokens=false, reste el valor de la expresión [delimitadores totales (11) - delimitadores consecutivos (4) + número de delimitadores dentro de las subcadenas (3)] del total real (19) para obtener el recuento de tokens. Como no devolvemos los delimitadores en este caso, ellos (sin aparecer consecutivamente o dentro de subcadenas) no nos sirven, y la fórmula anterior nos da el número total de tokens (9).

Recuerde estas dos fórmulas, que son el corazón del PowerfulTokenizer. Estas fórmulas funcionan para casi todos los casos respectivos. Sin embargo, si tiene requisitos más complejos que no son adecuados para estas fórmulas, debe considerar varios ejemplos para desarrollar su propia fórmula antes de apresurarse a codificar.

 // verifica si el delimitador está dentro de una subcadena for (int i = 1; i
   
    

El nextToken()método obtiene tokens usando StringTokenizer.nextToken, y busca el carácter de comillas dobles en el token. Si el método encuentra esos caracteres, obtiene más fichas hasta que no encuentra ninguna con comillas dobles. También almacena el token en una variable ( sPrevToken; consulte el código fuente) para verificar las apariciones consecutivas del delimitador. Si nextToken()encuentra tokens consecutivos que son iguales al delimitador, devuelve ""(cadena con longitud 0) como token.

De manera similar, el hasMoreTokens()método verifica si el número de tokens ya solicitados es menor que el número total de tokens.

Ahorre tiempo de desarrollo

Este artículo le ha enseñado cómo escribir fácilmente un poderoso tokenizador. Con estos conceptos, puede escribir tokenizadores complejos rápidamente, lo que le permite ahorrar un tiempo de desarrollo significativo.

Bhabani Padhi es un arquitecto y programador de Java que actualmente trabaja en el desarrollo de aplicaciones web y empresariales utilizando tecnología Java en UniteSys, Australia. Anteriormente, trabajó en Baltimore Technologies, Australia, en el desarrollo de productos de seguridad electrónica, y en Fujitsu, Australia, en un proyecto de desarrollo de servidores EJB. Los intereses de Bhabani incluyen la informática distribuida, el desarrollo de aplicaciones móviles y web utilizando tecnología Java.

Más información sobre este tema

  • Obtenga el código fuente de este consejo

    //images.techhive.com/downloads/idge/imported/article/jvw/2001/06/powerfultokenizer.java

  • Para obtener más información sobre BreakIterator

    //java.sun.com/products/jdk/1.2/docs/api/java/text/BreakIterator.html

  • Vea todos los consejos de Java anteriores y envíe los suyos

    //www.javaworld.com/javatips/jw-javatips.index.html

  • For more Intro Level articles, visit JavaWorld's Topical Index

    //www.javaworld.com/javaworld/topicalindex/jw-ti-introlevel.html

  • Learn Java from the ground up in JavaWorld's Java 101 column

    //www.javaworld.com/javaworld/topicalindex/jw-ti-java101.html

  • Java experts answer your toughest Java questions in JavaWorld's Java Q&A column

    //www.javaworld.com/javaworld/javaqa/javaqa-index.html

  • Sign up for the JavaWorld This Week free weekly email newsletter to find out what's new on JavaWorld

    //www.idg.net/jw-subscribe

Esta historia, "Consejo 112 de Java: Mejore la tokenización de cadenas ricas en información" fue publicada originalmente por JavaWorld.