Преброяване на думи в низ с Java

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. Цикли и String API

Другият метод е да има флаг, който да следи думите, които са били срещнати.

Задаваме знамето на WORD, когато срещаме нова дума и увеличаваме броя на думите, след което се връщаме към SEPARATOR, когато срещнем недума (пунктуационни или интервали).

Този подход ни дава същите резултати, които получихме при регулярните изрази:

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)  

Така че това, което направихме тук, е да позволим с една дума всички знаци и законни пунктуационни знаци, апострофът в този случай.

Вече можем да използваме този метод в нашата реализация:

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.