Проблемы с использованием цикла для поиска символа, введенного пользователем, в текстовом файле на Java

Вопрос или проблема

У меня проблемы с написанием программы для моего курса по Java. Инструкции к заданию следующие:

“Счетчик букв в файле:
Напишите программу, которая запрашивает у пользователя имя файла, а затем запрашивает у пользователя ввести символ. Программа должна считать и отобразить количество раз, которое указанный символ появляется в файле. Используйте Блокнот или другой текстовый редактор, чтобы создать пример файла, который можно использовать для тестирования программы.”

Текстовый файл, который предоставил нам профессор для тестирования этой программы, содержит 1307 строк случайным образом расставленных букв, которые программа должна обработать, и по какой-то причине я не могу заставить эту программу работать правильно. Я пытался использовать вещи, которые выходят за рамки того, что мы изучили на занятиях и в книге, но я совершенно потерян.

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

f
d
s
h
j

Вот мой код на данный момент (собранный в NetBeans 23):

package filelettercounter;

import java.util.Scanner;
import java.io.*;

public class FileLetterCounter {

    public static void main(String[] args) throws IOException {

        Scanner keyboard = new Scanner(System.in);
        
        System.out.print("Введите имя файла: ");
        String filename = keyboard.nextLine();
        
        File file = new File(filename);
        Scanner inputFile = new Scanner(file);
        
        do {
            
            int counter = 0;
            String line = inputFile.nextLine();
            
            System.out.print("Введите символ: ");
            String character = inputFile.nextLine();
            
            if(line.contains(character)) {
                counter++;
            }
            
            System.out.println("Символ " + character + " появляется " +
                    counter + " раз(а) в файле.");
            
            }while(inputFile.hasNext());
        
        inputFile.close();
        
        }
    }

Вывод печатает все 1307 строк, показывая, что каждая из них появляется 0 раз…

   Введите символ: Символ f появляется 0 раз(а) в файле.
   Введите символ: Символ d появляется 0 раз(а) в файле.
   Введите символ: Символ s появляется 0 раз(а) в файле.
   Введите символ: Символ h появляется 0 раз(а) в файле.
   Введите символ: Символ j появляется 0 раз(а) в файле.

…с исключением, выбрасываемым в середине вывода.

Исключение в потоке "main" java.util.NoSuchElementException: Строка не найдена
    в java.base/java.util.Scanner.nextLine(Scanner.java:1660)
    в filelettercounter.FileLetterCounter.main(FileLetterCounter.java:31)

Вывод, который мне нужно получить, выглядит примерно так:

Введите имя файла: "filename"
Введите символ: "character"
Символ (character) появляется (количество раз) раз(а) в файле.

Что касается ввода от любого из вас, кто намного опытнее меня, я не должен делать ничего сложного. Мы прошли лишь различные циклы (for, if/else if/else, do-while, while), накопление переменных и т.д. Это так легко, как только возможно. Так что, пожалуйста, никаких списков, массивов или разделителей, или чего-то подобного.

Три вещи, которые вы можете исправить:

  1. ваша переменная counter сбрасывается на ноль при каждой итерации

  2. может быть несколько вхождений целевого символа в каждой строке

  3. Сообщение вывода должно отображаться только после того, как вы пройдете через все строки

Удачи!

Понимание исключения

Давайте начнем с ошибки;

Она достаточно описательна с сообщением No line found.
Ваш код на FileLetterCounter.java:31 вероятно, не находится на той же строке, что и в вашем примере, я предполагаю, что он указывает на строку inputFile.nextLine();.

Эта строка – это место, где происходит ошибка в вашем коде, поэтому разумно начать с этого.

Далее мы видим вызов метода, который вызывает ошибку в вашем коде, Scanner.nextLine(). Документация для nextLine гласит следующее:

public String nextLine()

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

Поскольку этот метод продолжает искать разделитель строк в вводе, он может буферизовать весь ввод в поисках строки, которую нужно пропустить, если разделителей строк нет.

Возвращает: пропущенная строка

Вызывает:

  • NoSuchElementException – если строка не найдена
  • IllegalStateException – если этот сканер закрыт

Поскольку вы видите эту ошибку, возможно, ввода действительно нет и не может быть найдено никакой строки?

Решение

Давайте рассмотрим псевдокод:

подготовить файл и входные потоки.
цикл (по крайней мере один раз) пока сканер имеет другой токен во вводе:
    ...

На этом этапе вам следует проверить, работает ли всё. Возможно, начните с вывода каждого токена с помощью Scanner.next().
А теперь продолжаем с псевдокода:

цикл (по крайней мере один раз) пока сканер имеет другой токен во вводе:
    установить счетчик в ноль.
    перемещает сканер дальше по текущей строке и возвращает ввод
    попросите пользователя ввести символ.
    если собранная строка содержит экземпляр символа,
        тогда увеличьте счетчик на один.
    выведите счетчик.
закончите цикл и начните снова, используя тот же сканер.

Вот несколько вопросов, которые могут помочь:

  1. Если мы увеличим счетчик только на 1 перед выводом результата, почему он может быть чем-то иным, кроме 0 или 1?
  2. Если мы только перемещаем наш сканер вперед по строкам, что случится со счетом, если некоторые символы встречаются только в предыдущих строках?
  3. Что происходит, если пользователь запрашивает несколько раз, не исчерпаем ли мы строки?
  4. Почему мы проходим по строкам? Будет ли это как-то иначе, если бы мы просто проходили по символам? Кто знает, может, пользователь хочет подсчитать количество символов новой строки.
  5. Почему внешний цикл проверяет, есть ли у сканера еще токены с помощью hasNext(), но внутри цикла вся строка поглощается с помощью nextLine()?

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

Если вы ответите на эти вопросы и обсудите новый псевдокод, у вас будет рабочее решение.

Дополнительные выводы

При обучении программированию часто запускайте код. Добавьте 3-5 строк, возможно, используйте println для отладки, протестируйте это и повторите.

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

Ответ или решение

Конечно! Давайте исправим вашу программу, чтобы она выполняла поставленную задачу корректно. Как вы указали, вам нужно написать программу на Java, которая подсчитывает количество вхождений указанного символа в текстовом файле. Ваш код содержит несколько логических ошибок, и я постараюсь объяснить их и предложить исправленную версию кода.

Ошибки в вашем коде:

  1. Переменная counter сбрасывается на 0 в каждой итерации цикла, что мешает подсчету общего количества вхождений символа.
  2. Проверка наличия указанного символа в строке осуществляется с помощью line.contains(character), но в ваших данных может быть несколько вхождений символа в одной строке, что не учитывается.
  3. Ваша программа запрашивает символ от пользователя в каждой итерации, тогда как нужно запрашивать его только один раз перед началом подсчета.

Исправленная версия кода:

package filelettercounter;

import java.util.Scanner;
import java.io.*;

public class FileLetterCounter {

    public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in);

        System.out.print("Введите имя файла: ");
        String filename = keyboard.nextLine();

        System.out.print("Введите символ: ");
        String character = keyboard.nextLine();

        int counter = 0; // Инициализируем счетчик вне цикла

        try {
            File file = new File(filename);
            Scanner inputFile = new Scanner(file);

            // Проходим по каждой строке файла
            while (inputFile.hasNextLine()) {
                String line = inputFile.nextLine();

                // Подсчитываем вхождения символа в текущую строку
                for (char ch : line.toCharArray()) {
                    if (String.valueOf(ch).equalsIgnoreCase(character)) { // Сравниваем с учетом регистра
                        counter++;
                    }
                }
            }
            inputFile.close();

            // Выводим итоговый результат
            System.out.println("Символ '" + character + "' появляется " + counter + " раз(а) в файле.");

        } catch (FileNotFoundException e) {
            System.out.println("Ошибка: файл не найден.");
        } finally {
            keyboard.close(); // Закрываем сканер клавиатуры
        }
    }
}

Объяснение исправлений:

  1. Счетчик: Теперь переменная counter инициализируется вне цикла и будет суммироваться на каждой итерации, если символ найден в строке.
  2. Подсчет символов: Я использую цикл for, чтобы пройти через каждый символ в строке и сравнить его с введенным символом. Сравнение проводится с помощью equalsIgnoreCase, чтобы учесть регистры.
  3. Структура цикла: Мы используем один цикл для обработки всех строк в файле, и запрашиваем символ у пользователя один раз перед началом работы с файлом.
  4. Обработка исключений: Добавлен try-catch блок для обработки возможных исключений, если файл не найден.

Рекомендации:

  • Убедитесь, что файл, который вы вводите, существует в каталоге, из которого вы запускаете программу.
  • Если файлы большие, подумайте о том, как можно будет оптимизировать код для улучшения производительности, но для начального уровня это решение вполне эффективное и ясно.

Теперь, запустив эту программу, вы получите корректный вывод количества указанных символов из вашего текстового файла. Удачи с вашим проектом!

Оцените материал
Добавить комментарий

Капча загружается...