Перейти к основному содержимому

Подсчет слов в строке с помощью Java

· 4 мин. чтения

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 .