Проблема с зацикливанием при выборе длинных и коротких слов с использованием регулярного выражения

#java #regex #loops #sorting

Вопрос:

Мне нужно выбрать все слова максимальной длины и все слова минимальной длины.

Например:

 When I was younger, so much younger than today 
I never needed anybody's help in any way 
But now these days are gone, I'm not so self-assured 
Now I find I've changed my mind 
I've opened up the doors
 

Пример вывода:

 Min: I, s, m
Max: younger, anybody, assured, changed
 

Я уже разобрался в алгоритме, но он просто завершается после цикла сортировки (с int k), и я не могу понять, почему, потому что отладчик ничего не говорит из-за моего модульного тестирования. Это просто прекращается.
Не могли бы вы помочь мне понять, почему это не работает, пожалуйста?
P.S. Я не могу использовать контейнерные классы

 import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Part2 {

    public static void main(String[] args) {
        String input = Util.getInput("part2.txt");
        System.out.print(convert(input));
    }

    public static String convert(String input) {
        Pattern p = Pattern.compile("[A-zА-я] ");
        Matcher m = p.matcher(input);
        int i = 0;
        String[] arr = new String[100];
        while (m.find()){
            arr[i] = m.group(0);
            i  ;
        }
        StringBuilder longestResult = new StringBuilder();
        longestResult.append("Max: ");
        StringBuilder shortestResult = new StringBuilder();
        shortestResult.append("Min: ");
        int longest = 0;
        int shortest = arr[0].length();
        for (int k = 0; k < arr.length; k  ){
            if (arr[k].length() > longest){
                longest = arr[k].length();
            }
            if (arr[k].length() < shortest){
                shortest = arr[k].length();
            }
        }
        for ( String word : arr) {
            if (word.length() == longest) {
                longestResult.append(word   ", ");
            }
            if (word.length() == shortest) {
                shortestResult.append(word   ", ");
            }
        }
        return shortestResult.substring(1, shortestResult.length() - 2)  
                longestResult.substring(1, longestResult.length() - 2);
    }
}
 

Ответ №1:

Проблема, с которой вы столкнулись, довольно проста и легко отлаживается даже без отладчика. Ваш код имеет основной метод и может быть запущен выбранной вами средой разработки для отладки. Я предлагаю вам взглянуть на то, как отлаживать свой код, чтобы позже помочь вам с более сложными проблемами.


Просто взглянув на код, я вижу концептуальную проблему. Вы создаете массив строк, который частично заполняется в цикле while. Все эти элементы являются null таковыми, если только вы не присваиваете им значение. Таким образом, первые i элементы будут иметь набор строк, все остальные элементы будут null . После этого вы зацикливаетесь на всем массиве ( k < arr.length ). Проблема возникает внутри этого цикла for, потому что вы пытаетесь вызвать length() null элементы -. arr[k].length() вызовет исключение NullPointerException, начиная с i -го элемента. Та же проблема возникает позже снова с word.length() . Запуск вашего кода подтверждает вышесказанное:

Исключение в потоке «основной» java.lang.Исключение NullPointerException

на линии с. if (arr[k].length() > longest){ Один из способов исправить это (из многих) — выполнить итерацию только по элементам массива, которые на самом деле содержат значение ( 0 .. (i-1) ), например: for (int k = 0; k < (i - 1); k ){ . То же самое относится и ко второму циклу for.


Лучшим решением является использование String#split() , которое принимает регулярное выражение, определяющее, где разделить входные данные, и возвращает вам массив напрямую. Этот массив не имеет нулевых элементов и содержит ровно столько элементов, чтобы содержать результат. Вы можете заменить свой цикл while этим и получить решение, которое работает более чем на 100 слов без жестко заданного ограничения. Если вы используете это, вам не нужно изменять свои циклы.

 String[] arr = input.split("[\p{Punct}\s] ");
 

Дополнительные замечания:

  • Используйте a StringJoiner вместо a StringBuilder , чтобы иметь чистый способ получить значения, разделенные запятыми: StringJoiner longestWords = new StringJoiner(", ") затем используйте longestWords.add(word) и, наконец, сконструируйте результат "Max: " longestWords .
  • Вам нужно найти способ, чтобы в результате не было повторяющихся значений.