Java Tip 112: Подобрете токенизирането на богати на информация низове

Повечето програмисти на Java са използвали java.util.StringTokenizerкласа по едно или друго време. Това е удобен клас, който по същество токенизира (прекъсва) входния низ, базиран на разделител, и предоставя токени при поискване. (Токенизацията е актът на превръщане на последователности от символи в символи, които се разбират от вашата програма.)

Макар и удобен, StringTokenizerфункционалността на е ограничена. Класът просто търси разделителя във входния низ и прекъсва низа, след като разделителят бъде намерен. Той не проверява за условия като дали разделителят е в подниза, нито връща маркера като ""(дължина на низа 0), след като два последователни разделителя бъдат намерени във входа. За да изпълни тези ограничения, платформата Java 2 (JDK 1.2 нататък) идва с BreakIteratorкласа, който е подобрен токенизатор StringTokenizer. Тъй като такъв клас не присъства в JDK 1.1.x, разработчиците често отделят много време за писане на оригинален токенизатор, който отговаря на техните изисквания. В голям проект, включващ обработка на формат на данни, не е необичайно да намерите много такива персонализирани класове, които се носят.

Този съвет има за цел да ви насочи към писането на сложен токенизатор, използвайки съществуващия StringTokenizer.

Ограничения на StringTokenizer

Можете да създадете a, StringTokenizerкато използвате някой от следните три конструктора:

  1. StringTokenizer(String sInput): Прекъсвания на празно пространство ( " ", "\t", "\n").
  2. StringTokenizer(String sInput, String sDelimiter): Прекъсва sDelimiter.
  3. StringTokenizer(String sInput, String sDelimiter, boolean bReturnTokens): Прекъсва sDelimiter, но ако bReturnTokensе зададено на true, разделителят също се връща като знак.

Първият конструктор не проверява дали входният низ съдържа поднизове. Когато низът "hello. Today \"I am \" going to my home town"е токанизирани на празно пространство, резултатът е в жетони hello., Today, "I, am, ", going, вместо hello., Today, "I am ", going.

Вторият конструктор не проверява последователния вид на разделителите. Когато низът "book, author, publication,,,date published"е токанизирани на ","това StringTokenizerсе връща четири жетони със стойности book, author, publication, и date publishedвместо шестте стойности book, author, publication, "", "", и date published, когато е ""средство низ с дължина 0. За да получите шест, трябва да зададете StringTokenizerе bReturnTokensпараметър, за да е истина.

Характеристиката на настройката на параметъра на true е важна, тъй като дава представа за наличието на последователни разделители. Например, ако данните се получават динамично и се използват за актуализиране на таблица в база данни, където входните маркери се съпоставят със стойностите на колоните, тогава не можем да картографираме маркерите със колони на базата данни, тъй като не сме сигурни кои колони трябва да бъдат зададени до "". Например искаме да добавим записи към таблица с шест колони и входните данни съдържат два последователни разделителя. Резултатът от StringTokenizerв този случай е пет символа (тъй като два последователни разделителя представляват маркера "", който StringTokenizerпренебрегва) и трябва да зададем шест полета. Също така не знаем къде се появява последователният разделител, следователно коя колона трябва да бъде зададена "".

Третият конструктор няма да работи, ако самият маркер е равен (по дължина и стойност) на разделителя и е в подниз. Когато низът "book, author, publication,\",\",date published"е токанизирани (този низ съдържа ,в знак, което е същото като неговото разделител) на низ ,, резултатът е book, author, publication, ", ", date published(с шест жетони) вместо book, author, publication, ,(характер на запетая), date published(с пет жетона). Имайте предвид, че дори задаването на bReturnTokens(трети параметър на StringTokenizer) на true няма да ви помогне в този случай.

Основни нужди на токенизатор

Преди да се справите с кода, ще трябва да знаете основните нужди на добър токенизатор. Тъй като Java разработчици са свикнали с StringTokenizerкласа, добър tokenizer трябва да разполага с всички полезни методи, които осигуряват клас, като например hasMoreTokens(), nextToken(), countTokens().

Кодът за този съвет е прост и най-вече обяснителен. По принцип съм използвал StringTokenizerкласа (създаден с bReturnTokensset на true) вътрешно и съм предоставил методи, споменати по-горе. Тъй като в някои случаи разделителят се изисква като символи (много редки случаи), докато в някои не е, токенизаторът трябва да предостави разделителя като знак при поискване. Когато създавате PowerfulTokenizerобект, като предавате само входния низ и разделителя, той вътрешно използва a StringTokenizerс bReturnTokensset на true. (Причината за това е, ако a StringTokenizerе създаден, без да е bReturnTokensзададено на true, тогава той е ограничен при преодоляване на проблемите, посочени по-рано). За да се справя правилно с токенизатора, кодът bReturnTokensна няколко места проверява дали е зададен на true (изчислявайки общия брой на жетоните и nextToken()).

Както може би сте забелязали, PowerfulTokenizerизпълнява Enumerationинтерфейса, като по този начин реализира методите hasMoreElements()и nextElement(), които просто делегират повикването на hasMoreTokens()и nextToken(), съответно. (Чрез прилагане на Enumerationинтерфейса PowerfulTokenizerстава обратно съвместим с StringTokenizer.) Нека разгледаме пример. Кажете, че входният низ е, "hello, Today,,, \"I, am \", going to,,, \"buy, a, book\""а разделителят е ,. Този низ, когато токенизира връща стойности, както е показано в таблица 1:

Таблица 1: Стойности, върнати от токенизиран низ
Тип Брой токени Токени

StringTokenizer

(bReturnTokens = true)

19. hello:,: Today:,:,:,: "I:,: am ":,: going to:,:,:,: "buy:,: a:,: book"(тук символът :разделя жетоните)

PowerfulTokenizer

(bReturnTokens = true)

13 hello:,:Today:,:"":"":I, am:,:going to:,:"":"":buy a book(където ""означава низ с дължина 0)

PowerfulTokenizer

(bReturnTokens = false)

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

Входният низ съдържа 11 ,знака със запетая ( ), от които три са вътре в поднизовете, а четири се появяват последователно (както Today,,,прави два последователни появявания на запетая, като първата запетая е Todayразделител). Ето логиката при изчисляване на броя на жетоните в PowerfulTokenizerслучая:

  1. В случая bReturnTokens=trueумножете броя на разделителите в поднизовете по 2 и извадете тази сума от действителната сума, за да получите броя на жетоните. Причината за това, че за подниза "buy, a, book", StringTokenizerще върне пет маркера (т.е., ) buy:,:a:,:book, докато PowerfulTokenizerще върне един символ (т.е. buy, a, book). Разликата е четири (т.е. 2 * броя разделители вътре в подниза). Тази формула е подходяща за всеки подниз, съдържащ разделители. Имайте предвид специалния случай, когато самият маркер е равен на разделителя; това не трябва да намалява стойността на броенето.
  2. По същия начин, в случая на bReturnTokens=false, извадете стойността на израза [общо разделители (11) - последователни разделители (4) + брой разделители вътре в поднизовете (3)] от действителното общо (19), за да получите броя на жетоните. Тъй като в този случай ние не връщаме разделителите, те (без да се появяват последователно или вътре в поднизовете) не са ни от полза и горната формула ни дава общия брой символи (9).

Запомнете тези две формули, които са сърцето на PowerfulTokenizer. Тези формули работят за почти всички съответни случаи. Ако обаче имате по-сложни изисквания, които не са подходящи за тези формули, тогава трябва да разгледате различни примери, за да разработите своя собствена формула, преди да се впуснете в кодирането.

 // проверяваме дали разделителят е в рамките на подниз за (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 .