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&&[^']]+
находит любую длину знаков препинания или пробелов и игнорирует знак препинания в виде апострофа.
Чтобы узнать больше о регулярных выражениях, обратитесь к Регулярные выражения на ForEach .
4. Циклы и строковый
API
Другой метод состоит в том, чтобы иметь флаг, который отслеживает встречающиеся слова.
Мы устанавливаем флаг в СЛОВО
при встрече с новым словом и увеличиваем количество слов, а затем возвращаемся к СЕПАРАТОРУ
, когда встречаем не слово (знаки препинания или пробелы).
Этот подход дает нам те же результаты, что и с регулярными выражениями:
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;
}
Первое условие помечает слово, когда оно встречается с ним, и увеличивает значение счетчика. Второе условие проверяет, не является ли символ буквой, и устанавливает флаг в SEPARATOR
.
5. Вывод
В этом уроке мы рассмотрели способы подсчета слов с использованием нескольких подходов. Мы можем выбрать любой в зависимости от нашего конкретного варианта использования.
Как обычно, исходный код этого руководства можно найти на GitHub .