1. Обзор
В этом уроке мы рассмотрим различные способы подсчета слов в заданной строке с использованием Java.
2. Использование StringTokenizer
Простой способ подсчета слов в строке в Java-это использовать класс StringTokenizer :
assertEquals(3, new StringTokenizer("three blind mice").countTokens()); assertEquals(4, new StringTokenizer("see\thow\tthey\trun").countTokens());
Обратите внимание , что StringTokenizer автоматически заботится о пробелах для нас , таких как вкладки и возврат каретки.
Но в некоторых местах это может быть неправильно, например, дефисы:
assertEquals(7, new StringTokenizer("the farmer's wife--she was from Albuquerque").countTokens());
В этом случае мы хотели бы, чтобы “жена” и “она” были разными словами, но поскольку между ними нет пробелов, значения по умолчанию нас подводят.
К счастью, StringTokenizer поставляется с другим конструктором. Мы можем передать разделитель в конструктор, чтобы выполнить вышеуказанную работу:
assertEquals(7, new StringTokenizer("the farmer's wife--she was from Albuquerque", " -").countTokens());
Это удобно при попытке подсчитать слова в строке из что-то вроде CSV-файла:
assertEquals(10, new StringTokenizer("did,you,ever,see,such,a,sight,in,your,life", ",").countTokens());
Итак, StringTokenizer прост, и он помогает нам в этом большую часть пути.
Давайте посмотрим, какие дополнительные лошадиные силы могут дать нам регулярные выражения.
3. Регулярные выражения
Чтобы придумать осмысленное регулярное выражение для этой задачи, нам нужно определить, что мы считаем словом: слово начинается с буквы и заканчивается либо пробелом, либо знаком препинания .
Имея это в виду, учитывая строку, мы хотим разделить эту строку в каждой точке, где мы сталкиваемся с пробелами и знаками препинания, а затем подсчитать полученные слова.
assertEquals(7, countWordsUsingRegex("the farmer's wife--she was from Albuquerque"));
Давайте немного провернем все, чтобы увидеть силу регулярных выражений:
assertEquals(9, countWordsUsingRegex("no&one#should%ever-write-like,this;but:well"));
Нецелесообразно решать эту проблему, просто передавая разделитель в StringTokenizer , так как нам придется определить действительно длинный разделитель, чтобы попытаться перечислить все возможные знаки препинания.
Оказывается, нам действительно не нужно много делать, передавая регулярное выражение [\pP\s&&[^’]]+ к |/split методу класса String сделает трюк :
public static int countWordsUsingRegex(String arg) { if (arg == null) { return 0; } final String[] words = arg.split("[\pP\s&&[^']]+"); return words.length; }
Регулярное выражение [\pP\s&&[^’]]+ находит любую длину знаков препинания или пробелов и игнорирует знак препинания апострофа.
Чтобы узнать больше о регулярных выражениях, обратитесь к Регулярным выражениям в Baeldung .
4. Циклы и строковый API
Другой метод состоит в том, чтобы иметь флаг, который отслеживает слова, которые были встречены.
Мы устанавливаем флаг в WORD при обнаружении нового слова и увеличиваем количество слов, а затем возвращаемся к РАЗДЕЛИТЕЛЮ , когда мы сталкиваемся с не-словом (знаки препинания или пробелы).
Этот подход дает нам те же результаты, которые мы получили с регулярными выражениями:
assertEquals(9, countWordsManually("no&one#should%ever-write-like,this but well"));
Мы должны быть осторожны с особыми случаями , когда знаки препинания на самом деле не являются разделителями слов , например:
assertEquals(6, countWordsManually("the farmer's wife--she was from Albuquerque"));
То, что мы хотим здесь, – это считать “фермерское” одним словом, хотя апостроф “‘” является знаком препинания.
В версии регулярного выражения у нас была гибкость, чтобы определить, что не квалифицируется как символ, используя регулярное выражение. Но теперь, когда мы пишем нашу собственную реализацию, мы должны определить это исключение в отдельном методе :
private static boolean isAllowedInWord(char charAt) { return charAt == '\'' || Character.isLetter(charAt); }
Итак, то, что мы здесь сделали, – это разрешить в слове все символы и законные знаки препинания, в данном случае апостроф.
Теперь мы можем использовать этот метод в нашей реализации:
public static int countWordsManually(String arg) { if (arg == null) { return 0; } int flag = SEPARATOR; int count = 0; int stringLength = arg.length(); int characterCounter = 0; while (characterCounter < stringLength) { if (isAllowedInWord(arg.charAt(characterCounter)) && flag == SEPARATOR) { flag = WORD; count++; } else if (!isAllowedInWord(arg.charAt(characterCounter))) { flag = SEPARATOR; } characterCounter++; } return count; }
Первое условие помечает слово, когда оно встречается, и увеличивает счетчик. Второе условие проверяет, не является ли символ буквой, и устанавливает флаг в РАЗДЕЛИТЕЛЬ .
5. Заключение
В этом уроке мы рассмотрели способы подсчета слов с использованием нескольких подходов. Мы можем выбрать любой в зависимости от вашего конкретного случая использования.
Как обычно, исходный код этого учебника можно найти на GitHub .