410013796724260
• Webmoney
R335386147728
Z369087728698
Анализ текста, StringTokenizerКласс StringTokenizer пакета java.util предназначен для разложения строки на составляющие. Под токенацией понимается процесс разделения последовательности строки на части. Будучи удобным в использовании, StringTokenizer имеет серьезные функциональные ограничения. Так StringTokenizer раскладывает входную строку на части согласно переданных ему списка разделителей. Он не выполняет проверку на наличие разделителя внутри подстроки и не возвращает пустую строку нулевой длины, если во входном потоке обнаружена последовательность разделителей. Конструкторы класса StringTokenizerДля создания экземпляра StringTokenizer можно использовать один из следующих конструкторов:
Если строка "str" неопределена, т.е. равна null, то вызывается исключение NullPointerException. Пример использования класса StringTokenizer String s; s = "Тестовая строка, используемая для разложения на слова"; StringTokenizer st = new StringTokenizer(s, " \t\n\r,."); while (st.hasMoreTokens()) { // Выводим лексемы в консоль System.out.println(st.nextToken()); } Методы класса StringTokenizer, countTokens, hasMoreTokens, nextToken
Особенности использования класса StringTokenizerПервый конструктор с одним параметром str не выполняет проверку наличия в строке подстроки. Поэтому строка "Привет. Завтра \"21 сентября \" мы идем в театр." разбивается на следующие части: [Привет., Завтра, "21, сентября, ", мы, идем, в, театр.] // вместо [Привет., Завтра, "21 сентября " , мы, идем, в, театр.] Второй конструктор не отслеживает последовательное появление разделителя во входном потоке. Поэтому если строку "book, author, publication,,,date published" разложить на части, используя в качестве разделителя символ запятой ",", то получим набор из 4-x слов book, author, publication, date published вместо шести значений book, author, publication, "", "", date published , где "" означает строку нулевой длины. Чтобы получить все шесть частей необходимо использовать третий конструктор и установить параметр returnDelims в true. Особенность установки параметра returnDelims в true, является очень важной. Особенно когда текстовые данные поступают динамически и их необходимо раскладывать на составляющие и записывать в базу данных. В этом случае необходимо получить значения для всех полей таблицы. Третий конструктор не будет работать также и в том случае, когда информационная часть строки соответствует разделителю и находится внутри подстроки. Так, например после токенации следующей строки : "book, author, publication,\",\",date published" мы получим шесть частей book, author, publication, ", ", date published вместо пяти book, author, publication, ',', date published. В этом случае использование третьего конструктора с установленным в true значением returnDelims также не поможет. Адаптивный токенайзер, CustomTokenizerСоздать свой токенайзер на все случаи жизни - дело очень затратное по временным ресурсам, и не очень благодарное. Можно получить очень сложный код, бизнес-логика которого в скором времени забудется, и внесение дополнительных изменений потребует серьезных усилий. Ниже предложен подход решения одной из рассмотренных выше проблем - как исключить из анализа/разложения текст, обрамленный кавычками (двойными, одинарными) или разного рода скобками. Решение проблемы простое. Необходимо в токенайзер передать модифицированную строку для анализа, где проблемные участки текста заменены заглушками. А при извлечении токенов, вместо заглушек вернуть их исходное значение. То есть текст предварительно, перед передачей токенайзеру для разложения, обрабатывается и значение токена восстанавливается, перед возвращением. Листинг настраиваемого токенайзера : import java.util.Map; import java.util.HashMap; import java.util.StringTokenizer; public class CustomTokenizer { private StringTokenizer tokenizer = null; private int tokenNum = 0; private int totalTokens = 0; private final String QUOTE = "\""; private Map<String, String> substitutes; // шаблон заглушки private final String SUBSTITUTE = "__SUBSTITUTE__"; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Конструктор * @param text текстовая строка * @param delim строка разделителей */ public CustomTokenizer(String text, String delim) { substitutes = new HashMap <String, String>(); tokenizer = new StringTokenizer(setSubstitute(text), delim, true); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Конструктор * @param text текстовая строка * @param delim строка разделителей * @param includeDelim флаг включения разделителей * как токенов */ public CustomTokenizer(String str, String delim, boolean includeDelim) { tokenizer = new StringTokenizer(setSubstitute(str), delim, includeDelim); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Функция замены "проблемного" текста в кавычках * заглушками. Проблемный текст в строке, выделенный * двойными кавычками, заменяется * @param text исходный текст * @return измененный текст */ private String setSubstitute(final String text) { String temp = text; int id = 0; // Определение начала и конца проблемного кода int startQuote = temp.indexOf(QUOTE, 0); int endQuote = -1; if (startQuote >= 0 ) endQuote = temp.indexOf(QUOTE, startQuote + 1); // Цикл перебора текста while (((startQuote >= 0) && (endQuote > startQuote))) { // Извлечение проблемного текста String txt; txt = temp.substring(startQuote, endQuote + 1); // Определение ключа String key = SUBSTITUTE + String.valueOf(id++); // Замена проблемного текста заглушкой temp = temp.replaceFirst(txt, key); // Сохранение проблемного текста с ключом substitutes.put(key, txt); // Подготовка к следующему циклу startQuote = temp.indexOf(QUOTE, 0); if (startQuote >= 0) endQuote = temp.indexOf(QUOTE,startQuote+1); } return temp; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Функция возвращения следующего токена строки * @return следующий токен */ public String nextToken() { String sToken = tokenizer.nextToken(); if (substitutes.containsKey(sToken.trim())) sToken = substitutes.get(sToken.trim()); tokenNum++; return sToken; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Функция проверки наличия оставшихся токенов * @return логическое значение true, если имеется * еще токен */ public boolean hasMoreTokens() { if (totalTokens == 0) totalTokens = countTokens(); return (tokenNum < totalTokens); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Функция проверки наличия оставшихся токенов * @return логическое значение true, если имеется * еще токен */ public boolean hasMoreElements() { return hasMoreTokens(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Функция получения следующего токена * @return текущий токен строки типа Object */ public Object nextElement() { return nextToken(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Функция определения количества токенов в строке * @return количество токенов */ public int countTokens() { return tokenizer.countTokens(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public static void main(String[] args) { String string; // string = "hi, hello,, \"how, are, qrt, u\", good, " // + "\"fine, hty, great\", data"; // string = "hi, how, \"are, u\", hello, \"how, are\", u"; string = "Привет. Завтра \"21 сентября \" " + "мы идем в театр."; System.out.println("~~~ Исходная строка ~~~\n" + string); CustomTokenizer tokenizer; tokenizer = new CustomTokenizer(string, " .", false); System.out.println("\nколичество токенов = " + tokenizer.countTokens()); System.out.println("\n~~~ Список токенов ~~~"); int i = 0; while(tokenizer.hasMoreTokens()) System.out.println("" + ++i + " = " + tokenizer.nextToken()); } } В данном токенайзере имеется две функции, код которых можно модифицировать под разные случаи жизни:
Результат выполнения программы выведет в консоль следующую информацию : ~~~ Исходная строка ~~~ Привет. Завтра "21 сентября " мы идем в театр. количество токенов = 7 ~~~ Список токенов ~~~ 1 = Привет 2 = Завтра 3 = "21 сентября " 4 = мы 5 = идем 6 = в 7 = театр |