Astuce Java 112: Améliorez la tokenisation des chaînes riches en informations

La plupart des programmeurs Java ont utilisé la java.util.StringTokenizerclasse à un moment ou à un autre. C'est une classe pratique qui tokenise (casse) la chaîne d'entrée en fonction d'un séparateur et fournit les jetons sur demande. (La tokenisation consiste à transformer des séquences de caractères en jetons compris par votre programme.)

Bien que pratique, StringTokenizerses fonctionnalités sont limitées. La classe recherche simplement le délimiteur dans la chaîne d'entrée et rompt la chaîne une fois le délimiteur trouvé. Il ne vérifie pas les conditions telles que si le délimiteur est dans une sous-chaîne, ni ne renvoie le jeton comme ""(longueur de chaîne 0) une fois que deux délimiteurs consécutifs sont trouvés dans l'entrée. Pour répondre à ces limitations, la plate-forme Java 2 (à partir de JDK 1.2) est fournie avec la BreakIteratorclasse, qui est un tokenizer amélioré StringTokenizer. Comme une telle classe n'est pas présente dans JDK 1.1.x, les développeurs passent souvent beaucoup de temps à écrire un tokenizer original qui répond à leurs exigences. Dans un grand projet impliquant la gestion du format de données, il n'est pas rare de trouver de nombreuses classes personnalisées de ce type flottant.

Cette astuce vise à vous guider dans l'écriture d'un tokenizer sophistiqué, en utilisant l'existant StringTokenizer.

Limitations de StringTokenizer

Vous pouvez créer un StringTokenizeren utilisant l'un des trois constructeurs suivants:

  1. StringTokenizer(String sInput): Pause sur un espace blanc ( " ", "\t", "\n").
  2. StringTokenizer(String sInput, String sDelimiter): Pause sDelimiter.
  3. StringTokenizer(String sInput, String sDelimiter, boolean bReturnTokens): S'interrompt sDelimiter, mais si bReturnTokensest défini sur true, le délimiteur est également renvoyé sous forme de jeton.

Le premier constructeur ne vérifie pas si la chaîne d'entrée contient des sous-chaînes. Lorsque la chaîne "hello. Today \"I am \" going to my home town"est segmenté sur l' espace blanc, le résultat est en jetons hello., Today, "I, am, ", au goinglieu de, hello., Today, "I am ", going.

Le deuxième constructeur ne vérifie pas l'apparence consécutive des délimiteurs. Lorsque la chaîne "book, author, publication,,,date published"est segmenté sur ",", les StringTokenizerrendements quatre jetons avec des valeurs book, author, publicationet au date publishedlieu des six valeurs book, author, publication, "", ""et date publishedoù des ""moyens chaîne de longueur 0. Pour obtenir six, vous devez définir le StringTokenizers » bReturnTokensparamètre true.

La fonction de définition du paramètre sur true est importante car elle donne une idée de la présence de délimiteurs consécutifs. Par exemple, si les données sont obtenues dynamiquement et utilisées pour mettre à jour une table dans une base de données, où les jetons d'entrée correspondent aux valeurs de colonne, nous ne pouvons pas mapper les jetons avec des colonnes de base de données car nous ne savons pas quelles colonnes doivent être définies à "". Par exemple, nous voulons ajouter des enregistrements à une table avec six colonnes et les données d'entrée contiennent deux délimiteurs consécutifs. Le résultat StringTokenizerdans ce cas est de cinq jetons (car deux délimiteurs consécutifs représentent le jeton "", ce qui StringTokenizernéglige), et nous devons définir six champs. Nous ne savons pas non plus où apparaît le délimiteur consécutif, donc quelle colonne doit être définie "".

Le troisième constructeur ne fonctionnera pas si un jeton lui-même est égal (en longueur et en valeur) au délimiteur et se trouve dans une sous-chaîne. Lorsque la chaîne "book, author, publication,\",\",date published"tokenizé (cette chaîne contient ,un jeton, qui est le même que son delimiter) sur la chaîne ,, le résultat est book, author, publication, ", ", date published(avec six jetons) au lieu de book, author, publication, ,(la virgule), date published(avec cinq jetons). Remarquez que même définir le bReturnTokens(troisième paramètre sur StringTokenizer) sur true ne vous aidera pas dans ce cas.

Besoins de base d'un tokenizer

Avant de traiter le code, vous devez connaître les besoins de base d'un bon tokenizer. Étant donné que les développeurs Java sont utilisés pour la StringTokenizerclasse, un bon tokenizer devrait avoir toutes les méthodes utiles qui fournit la classe, comme hasMoreTokens(), nextToken(), countTokens().

Le code de cette astuce est simple et souvent explicite. Fondamentalement, j'ai utilisé la StringTokenizerclasse (créée avec bReturnTokensla valeur true) en interne et fourni les méthodes mentionnées ci-dessus. Étant donné que dans certains cas, le délimiteur est requis en tant que jetons (cas très rares) alors que dans certains ce n'est pas le cas, le tokenizer doit fournir le délimiteur en tant que jeton sur demande. Lorsque vous créez un PowerfulTokenizerobjet, en ne transmettant que la chaîne d'entrée et le délimiteur, il utilise en interne un StringTokenizeravec bReturnTokensdéfini sur true. (La raison en est que si a StringTokenizerest créé sans être bReturnTokensdéfini sur true, il est alors limité pour surmonter les problèmes mentionnés précédemment). Pour gérer correctement le tokenizer, le code vérifie s'il bReturnTokensest défini sur true à quelques endroits (calcul du nombre total de jetons et nextToken()).

Comme vous l'avez peut-être remarqué, PowerfulTokenizerimplémente l' Enumerationinterface, implémentant ainsi les méthodes hasMoreElements()et nextElement()qui délèguent simplement l'appel à hasMoreTokens()et nextToken(), respectivement. (En implémentant l' Enumerationinterface, PowerfulTokenizerdevient rétrocompatible avec StringTokenizer.) Prenons un exemple. Disons que la chaîne d'entrée est "hello, Today,,, \"I, am \", going to,,, \"buy, a, book\""et que le délimiteur est ,. Cette chaîne lorsqu'elle est tokenisée renvoie des valeurs comme indiqué dans le tableau 1:

Tableau 1: Valeurs renvoyées par la chaîne tokenisée
Type Nombre de jetons Jetons

StringTokenizer

(bReturnTokens = true)

19 hello:,: Today:,:,:,: "I:,: am ":,: going to:,:,:,: "buy:,: a:,: book"(ici le caractère :sépare les jetons)

PowerfulTokenizer

(bReturnTokens = true)

13 hello:,:Today:,:"":"":I, am:,:going to:,:"":"":buy a book(où ""signifie chaîne de longueur 0)

PowerfulTokenizer

(bReturnTokens = false)

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

La chaîne d'entrée contient 11 caractères virgule ( ,), dont trois sont à l'intérieur des sous-chaînes et quatre apparaissent consécutivement (comme cela Today,,,fait deux apparitions consécutives de virgules, la première virgule étant Todayle délimiteur de). Voici la logique du calcul du nombre de jetons dans le PowerfulTokenizercas:

  1. Dans le cas de bReturnTokens=true, multipliez le nombre de délimiteurs dans les sous-chaînes par 2 et soustrayez ce montant du total réel pour obtenir le nombre de jetons. La raison, pour la sous - chaîne "buy, a, book", StringTokenizerretournera cinq jetons (c. -à- buy:,:a:,:book), tout PowerfulTokenizerretourne un jeton (c. -à- buy, a, book). La différence est de quatre (c'est-à-dire, 2 * nombre de délimiteurs à l'intérieur de la sous-chaîne). Cette formule est valable pour toute sous-chaîne contenant des délimiteurs. Soyez conscient du cas particulier où le jeton lui-même est égal au délimiteur; cela ne devrait pas décrémenter la valeur de comptage.
  2. De même, pour le cas de bReturnTokens=false, soustrayez la valeur de l'expression [délimiteurs totaux (11) - délimiteurs consécutifs (4) + nombre de délimiteurs à l'intérieur des sous-chaînes (3)] du total réel (19) pour obtenir le nombre de jetons. Puisque nous ne retournons pas les délimiteurs dans ce cas, ils (sans apparaître consécutivement ou à l'intérieur de sous-chaînes) ne nous sont d'aucune utilité, et la formule ci-dessus nous donne le nombre total de jetons (9).

Rappelez-vous ces deux formules, qui sont au cœur du PowerfulTokenizer. Ces formules fonctionnent pour presque tous les cas respectifs. Cependant, si vous avez des exigences plus complexes qui ne conviennent pas à ces formules, vous devez envisager divers exemples pour développer votre propre formule avant de vous lancer dans le codage.

 // vérifie si le délimiteur est dans une sous-chaîne pour (int i = 1; i
   
    

The nextToken() method gets tokens by using StringTokenizer.nextToken, and checks for the double quote character in the token. If the method finds those characters, it gets more tokens until it doesn't find any with a double quote. It also stores the token in a variable (sPrevToken; see source code) for checking consecutive delimiter appearances. If nextToken() finds consecutive tokens that are equal to the delimiter, then it returns "" (string with length 0) as the token.

Similarly, the hasMoreTokens() method checks whether the number of tokens already requested is less than the total number of tokens.

Save development time

This article has taught you how to easily write a powerful tokenizer. Using these concepts, you can write complex tokenizers quickly, thus saving you significant development time.

Bhabani Padhi is a Java architect and programmer currently working on Web and enterprise application development using Java technology at UniteSys, Australia. Previously he worked at Baltimore Technologies, Australia on e-security product development and at Fujitsu, Australia on an EJB server development project. Bhabani's interests include distributed computing, mobile, and Web application development using Java technology.

Learn more about this topic

  • Get the source code for this tip

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

  • For more information on BreakIterator

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

  • View all previous Java Tips and submit your own

    //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

This story, "Java Tip 112: Improve tokenization of information-rich strings" was originally published by JavaWorld .