Contar palabras en una cadena con Java

1. Información general

En este tutorial, repasaremos diferentes formas de contar palabras en una cadena dada usando Java.

2. Usando StringTokenizer

Una forma sencilla de contar palabras en una cadena en Java es usar la clase StringTokenizer :

assertEquals(3, new StringTokenizer("three blind mice").countTokens()); assertEquals(4, new StringTokenizer("see\thow\tthey\trun").countTokens());

Tenga en cuenta que StringTokenizer se encarga automáticamente de los espacios en blanco , como las pestañas y los retornos de carro.

Pero, podría fallar en algunos lugares, como guiones:

assertEquals(7, new StringTokenizer("the farmer's wife--she was from Albuquerque").countTokens());

En este caso, querríamos que "esposa" y "ella" fueran palabras diferentes, pero como no hay espacios en blanco entre ellos, los valores predeterminados nos fallan.

Afortunadamente, StringTokenizer se envía con otro constructor. Podemos pasar un delimitador al constructor para que funcione lo anterior:

assertEquals(7, new StringTokenizer("the farmer's wife--she was from Albuquerque", " -").countTokens());

Esto es útil cuando se intenta contar las palabras en una cadena de algo como un archivo CSV:

assertEquals(10, new StringTokenizer("did,you,ever,see,such,a,sight,in,your,life", ",").countTokens());

Entonces, StringTokenizer es simple y nos lleva a la mayor parte del camino.

Veamos qué potencia extra nos pueden dar las expresiones regulares.

3. Expresiones regulares

Para que podamos encontrar una expresión regular significativa para esta tarea, necesitamos definir lo que consideramos una palabra: una palabra comienza con una letra y termina con un carácter de espacio o un signo de puntuación .

Con esto en mente, dada una cadena, lo que queremos hacer es dividir esa cadena en cada punto en el que encontremos espacios y signos de puntuación, luego contar las palabras resultantes.

assertEquals(7, countWordsUsingRegex("the farmer's wife--she was from Albuquerque"));

Vamos a subir un poco las cosas para ver el poder de las expresiones regulares:

assertEquals(9, countWordsUsingRegex("no&one#should%ever-write-like,this;but:well"));

No es práctico resolver esto simplemente pasando un delimitador a StringTokenizer ya que tendríamos que definir un delimitador realmente largo para intentar enumerar todos los signos de puntuación posibles.

Resulta que realmente no tenemos que hacer mucho, pasar la expresión regular [\ pP \ s && [^ ']] + al método de división de la clase String hará el truco :

public static int countWordsUsingRegex(String arg) { if (arg == null) { return 0; } final String[] words = arg.split("[\pP\s&&[^']]+"); return words.length; }

La expresión regular [\ pP \ s && [^ ']] + busca cualquier longitud de signos de puntuación o espacios e ignora el signo de puntuación del apóstrofo.

Para obtener más información sobre las expresiones regulares, consulte Expresiones regulares en Baeldung.

4. Bucles y la API de cadenas

El otro método es tener una bandera que realice un seguimiento de las palabras que se han encontrado.

Establecemos la bandera en WORD cuando encontramos una nueva palabra e incrementamos el recuento de palabras, luego volvemos a SEPARATOR cuando encontramos una no-palabra (signos de puntuación o espacios).

Este enfoque nos da los mismos resultados que obtuvimos con las expresiones regulares:

assertEquals(9, countWordsManually("no&one#should%ever-write-like,this but well")); 

Tenemos que tener cuidado con casos especiales en los que los signos de puntuación no son realmente separadores de palabras , por ejemplo:

assertEquals(6, countWordsManually("the farmer's wife--she was from Albuquerque"));

Lo que queremos aquí es contar "agricultor" como una palabra, aunque el apóstrofe "'" es un signo de puntuación.

En la versión de expresiones regulares, tuvimos la flexibilidad de definir lo que no califica como un personaje usando la expresión regular. Pero ahora que estamos escribiendo nuestra propia implementación, tenemos que definir esta exclusión en un método separado :

private static boolean isAllowedInWord(char charAt)  

Entonces, lo que hemos hecho aquí es permitir en una palabra todos los caracteres y signos de puntuación legales, el apóstrofe en este caso.

Ahora podemos usar este método en nuestra implementación:

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; }

La primera condición marca una palabra cuando encuentra una e incrementa el contador. La segunda condición comprueba si el carácter no es una letra y establece la bandera en SEPARATOR .

5. Conclusión

En este tutorial, hemos visto formas de contar palabras usando varios enfoques. Podemos elegir cualquiera dependiendo de nuestro caso de uso particular.

Como de costumbre, el código fuente de este tutorial se puede encontrar en GitHub.