Contando palavras em uma string
1. Visão geral
Neste tutorial, examinaremosdifferent ways of counting words in a given string usando Java.
2. UsandoStringTokenizer
A simple way to count words em uma string em Java deve usar a classeStringTokenizer:
assertEquals(3, new StringTokenizer("three blind mice").countTokens());
assertEquals(4, new StringTokenizer("see\thow\tthey\trun").countTokens());
Observe queStringTokenizer automaticamente levacare of whitespace for us, como tabulações e retornos de carro.
Mas, pode parecer bobagem em alguns lugares, como hífens:
assertEquals(7, new StringTokenizer("the farmer's wife--she was from Albuquerque").countTokens());
Neste caso, gostaríamos que "esposa" e "ela" fossem palavras diferentes, mas como não há espaço em branco entre elas, os padrões falham.
Felizmente,StringTokenizer navega com outro construtor. We can pass a delimiter no construtor para fazer o trabalho acima:
assertEquals(7, new StringTokenizer("the farmer's wife--she was from Albuquerque", " -").countTokens());
Isso é útil ao tentar contar as palavras em uma string desomething like a CSV file:
assertEquals(10, new StringTokenizer("did,you,ever,see,such,a,sight,in,your,life", ",").countTokens());
Portanto,StringTokenizer é simples e nos leva a maior parte do caminho.
Vamos ver o que as expressões regulares de potência extra podem nos dar.
3. Expressões regulares
Para que possamos encontrar uma expressão regular significativa para essa tarefa, precisamos definir o que consideramos uma palavra:a word starts with a letter and ends either with a space character or a punctuation mark.
Com isso em mente, dada uma string, o que queremos fazer é dividi-la em cada ponto que encontrarmos espaços e sinais de pontuação e, em seguida, conte as palavras resultantes.
assertEquals(7, countWordsUsingRegex("the farmer's wife--she was from Albuquerque"));
Vamos aumentar um pouco as coisas para ver o poder do regex:
assertEquals(9, countWordsUsingRegex("no&one#should%ever-write-like,this;but:well"));
Não é prático resolver este apenas passando um delimitador paraStringTokenizer, pois teríamos que definir um delimitador realmente longo para tentar listar todos os sinais de pontuação possíveis.
Acontece que realmente não precisamos fazer muito,passing the regex[\pP\s&&[^']]+to thesplitmethod of theStringclass will do the trick:
public static int countWordsUsingRegex(String arg) {
if (arg == null) {
return 0;
}
final String[] words = arg.split("[\pP\s&&[^']]+");
return words.length;
}
O regex [\pP\s&&[^']]+ localiza qualquer comprimento de sinais de pontuação ou espaços e ignora o sinal de pontuação de apóstrofo.
Para saber mais sobre expressões regulares, consulteRegular Expressions on example.
4. Loops eString API
O outro método é ter um sinalizador que monitora as palavras que foram encontradas.
Definimos o sinalizador paraWORD ao encontrar uma nova palavra e incrementamos a contagem de palavras, depois voltamos paraSEPARATOR quando encontramos uma não palavra (pontuação ou caracteres de espaço).
Essa abordagem nos fornece os mesmos resultados que obtivemos com expressões regulares:
assertEquals(9, countWordsManually("no&one#should%ever-write-like,this but well"));
We do have to be careful with special cases where punctuation marks are not really word separators, por exemplo:
assertEquals(6, countWordsManually("the farmer's wife--she was from Albuquerque"));
O que queremos aqui é contar "agricultor" como uma palavra, embora o apóstrofo "‘ "seja um sinal de pontuação.
Na versão regex, tivemos a flexibilidade de definir o que não se qualifica como um caractere usando a regex. Mas agora que estamos escrevendo nossa própria implementação,we have to define this exclusion in a separate method:
private static boolean isAllowedInWord(char charAt) {
return charAt == '\'' || Character.isLetter(charAt);
}
Então, o que fizemos aqui é permitir em uma palavra todos os caracteres e sinais de pontuação legais, o apóstrofo neste caso.
Agora podemos usar esse método em nossa implementação:
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;
}
A primeira condição marca uma palavra quando encontra uma e incrementa o contador. A segunda condição verifica se o caractere não é uma letra e define o sinalizador paraSEPARATOR.
5. Conclusão
Neste tutorial, vimos maneiras de contar palavras usando várias abordagens. Podemos escolher qualquer um, dependendo do nosso caso de uso específico.
Como de costume, o código-fonte deste tutorial pode ser encontradoover on GitHub.