Улучшаем токенизацию( Бхабани Падхи ) Превращаем StringTokenizer в мощный токенизатор. Tips 'N Tricks
Многие Java-программисты время от времени используют класс java.util.StringTokenizer. Это полезный класс, токенизирующий (разбивающий) строку символов на части, основываясь на разделителе, и возвращающий их по необходимости. (Токенизация является процессом разделения последовательности строки на части, понимаемые Вашей программой). Будучи удобным в использовании, StringTokenizer функционально ограничен. Класс просто ищет во входной строке разделитель и отделяет от нее часть, как только он найден. Он не выполняет проверку на наличие разделителя внутри подстроки и не возвращает строку типа "" (пустая строка нулевой длины) если во входном потоке обнаружена последовательность разделителей. Чтобы восполнить эти ограничения в Java 2 (JDK 1.2 и выше) включен класс BreakIterator, представляющий собой улучшенный токенизатор по сравнению с StringTokenizer. Однако т.к. этот класс отсутствует в JDK 1.1.X, разработчики часто проводят много времени, разрабатывая собственные классы, удовлетворяющие их требованиям. Большие проекты, так или иначе, включают обработку данных, и нет ничего необычного в том, что на рынке присутствует достаточно классов для этих целей. Эта статья преследует собой цель продемонстрировать Вам, как разрабатывать токенизаторы, базирующиеся на StringTokenizer. Ограничения StringTokenizerВы можете создать экземпляр StringTokenizer, используя один из следующих конструкторов:
Первый конструктор не выполняет проверку наличия в строке подстроки. Поэтому строка "hello. Today \"I am \" going to my home town" разбивается на следующие части: hello., Today, "I, am, ", going, вместо: hello., Today, "I am ", going. Второй конструктор не отслеживает последовательное появление разделителя во входном потоке. Поэтому строка "book, author, publication,,,date published" делится (разделитель ",") на book, author, publication, and date published вместо шести значений book, author, publication, "", "", and date published, где "" означает строку нулевой длины. Чтобы поучить все шесть частей Вы должны установить параметр bReturnTokens в true. Особенность установки параметра в true, является очень важной т.к. наводит на мысль о возможном присутствии последовательного расположения разделителей. например, если данные поступают в динамическом режиме и предназначены для записи в таблицу базы данных, где введенные части строки ассоциированы с полями таблицы, то мы не можем правильно выполнить запись пока не будем уверены в том, какому полю таблицы соответствует пустая строка "". Рассмотрим пример, когда мы хотим добавить запись в таблицу с шестью полями, а входной поток содержит два последовательных разделителя. Результат, полученный из StringTokenizer, в этом случае будет состоять из пяти частей (т.к. два последовательных разделителя представляют "", которым StringTokenizer пренебрегает), а мы должны связать их с шестью полями. Т.к. мы не знаем, где встретилась последовательность разделителей, то соответственно, мы не можем принять решение, к какому полю относится пустая строка. Третий конструктор не будет работать также и в случае, когда информационная часть строки соответствует разделителю и находится внутри подстроки. После токенизации строки "book, author, publication,\",\",date published" мы получим book, author, publication, ", ", date published (шесть частей), вместо пяти (book, author, publication, , (the comma character), date published). Между прочим, установка bReturnTokens (третьего параметра конструктора) в true в этом случае не поможет. Базовые требования к токенизаторуПрежде чем начать разработку, Вы должны осознавать требования предъявляемые к хорошему токенизатору. Т.к. Java разработчики привыкли к использованию класса StringTokenizer, хороший токенизатор должен иметь полезные методы присущие его предшественнику, такие как: hasMoreTokens(), nextToken(), countTokens(). Исходный код, приведенный в этой статье, достаточно прост. За основу я взял StringTokenizer (с параметром bReturnTokens, равным true), используемый внутри и методами описанными выше. В некоторых случаях разделитель может выступать в качестве необходимой информационной части (очень редко), в некоторых нет, поэтому токенизатор должен будет поддерживать эту возможность при необходимости. Когда Вы создаете объект PowerfulTokenizer, передавая только строку и разделитель, он использует StringTokenizer с bReturnTokens, равным true. Чтобы токенизатор работал правильно, исходный код проверяет эту установку в нескольких местах (определяя количество частей в nextToken()). Как Вы могли заметить, PowerfulTokenizer воплощает интерфейс Enumeration, реализуя т.о. методы hasMoreElements() и nextElement(), которые просто делегируют вызовы к hasMoreTokens() и nextToken(), соответственно. Воплощая интерфейс Enumeration, класс становится обратно совместимым с StringTokenizer. Давайте рассмотрим пример. Возьмем, скажем, строку: "hello, Today,,, \"I, am \", going to,,, \"buy, a, book\"", а в качестве разделителя символ запятой. Результаты токенизации приведены в Таблице 1.
Таблица 1. Результаты токенизации строки
Исходная строка содержит 11 запятых (,), три из которых находятся внутри подстрок, а четыре появляются последовательно (так Today,,, содержит два последовательных разделителя, т.к. первая запятая относится к Tobay). Это и является примером организации подсчета количества частей в случае PowerfulTokenizer:
// check whether the delimiter is within a substring for (int i=1; i<aiIndex.length; i++) { iIndex = sInput.indexOf(sDelim, iIndex+1); if (iIndex == -1) break; // if the delimiter is within a substring, then parse up to the // end of the substring. while (sInput.substring(iIndex-iLen, iIndex).equals(sDelim)) { iNextIndex = sInput.indexOf(sDelim, iIndex+1); if (iNextIndex == -1) break; iIndex = iNextIndex; } aiIndex[i] = iIndex; //System.out.println("aiIndex[" + i + "] = " + iIndex); if (isWithinQuotes(iIndex)) { if (bIncludeDelim) iTokens -= 2; else iTokens -= 1; } } Метод countTokens() проверяет, содержит ли исходная строка двойные кавычки. Если содержит, то количество частей уменьшается и индекс приравнивается индексу следующих кавычек (как показано в следующем листинге). Если bReturnTokens = false, тогда уменьшаем количество частей на количество непоследовательных разделителей во входной строке. // return "" as token if consecutive delimiters are found. if ( (sPrevToken.equals(sDelim)) && (sToken.equals(sDelim)) ) { sPrevToken = sToken; iTokenNo++; return ""; } // check whether the token itself is equal to the delimiter if ( (sToken.trim().startsWith("\"")) && (sToken.length() == 1) ) { // this is a special case when token itself is equal to delimiter String sNextToken = oTokenizer.nextToken(); while (!sNextToken.trim().endsWith("\"")) { sToken += sNextToken; sNextToken = oTokenizer.nextToken(); } sToken += sNextToken; sPrevToken = sToken; iTokenNo++; return sToken.substring(1, sToken.length()-1); } // check whether there is a substring inside the string else if ( (sToken.trim().startsWith("\"")) && (!((sToken.trim().endsWith("\"")) && (!sToken.trim().endsWith("\"\"")))) ) { if (oTokenizer.hasMoreTokens()) { String sNextToken = oTokenizer.nextToken(); // check for presence of "\"\"" while (!((sNextToken.trim().endsWith("\"")) && (!sNextToken.trim().endsWith("\"\""))) ) { sToken += sNextToken; if (!oTokenizer.hasMoreTokens()) { sNextToken = ""; break; } sNextToken = oTokenizer.nextToken(); } sToken += sNextToken; } } Метод nextToken() получает количество частей, используя StringTokenizer.nextToken(), и проверяет наличие двойных кавычек внутри каждой части. Если метод находит их, то поиск ведется до тех пор, пока не встретится вторая пара кавычек. Метод также сохраняет часть в переменной (sPrevToken), для проверки последовательного появления разделителей. Если nextToken() обнаруживает последовательное появление разделителей в потоке, он возвращает ""(строку нулевой длины). Подобным образом, hasMoreTokens() проверяет меньше ли, затребованное количество частей общему. Сократите время разработкиЭта статья демонстрирует принципы построения мощных токенизаторов. Используя эти концепции, Вы легко можете создавать новые токенизаторы, не тратя драгоценное время. Об автореBhabani Padhi — программист и архитектор на Java сейчас разрабатывает приложения для веба и предприятий используя технологию Java в UniteSys, Австралия. Ранее он работал в Baltimore Technologies, Австралия над системой обеспечения безопасности и в Fujitsu, Australia над разработкой сервера EJB. Бхабани интересуется распределенными вычислениями, мобильными и веб-приложениями с использованием технологии Java. Ресурсы
Reprinted with permission from JavaWorld magazine. Copyright © ITworld.com, Inc., an IDG Communications company. Source: http://www.javaworld.com/ javaworld/javatips/jw-javatip112.html | |||||||||||||
| |||||||||||||
Views: 1935 | |