Почему BufferedReader.readLine может читать строку, которая не имеет разделителя строк

Считывает текст. Линия считается завершенной любым из строк ('\n'), возвратом каретки ('\ r') или возвратом каретки, за которым следует сразу строка-строка.------ javadoc 1.8

Затем у меня есть текстовый файл:

the first line
the second line

примечание: последний символ строки seond - 'e', ​​то есть не существует возврата каретки.

то вот мой демо-код.

public void process() throws IOException{
    BufferedReader br = new BufferedReader(new FileReader("demo.txt"));
    String line;
    while((line=br.readLine())!=null){
        System.out.println(line);
    }
    br.close();
}

реальный вывод:

 the first line
 the second line

то мой вопрос в том, почему метод readLine может получить вторую строку для него, не имеет разделителя строк (\n или \r или\n\r).
Я знаю, что существует конец файла (EOF), но, похоже, javadoc не сообщает, что EOF явно является разделителем строк.

Если я использую Scanner вместо BufferedReader, код ниже:

public void testScan() throws IOException{
    Scanner scan = new Scanner(new FileInputStream("demo.txt"));
    String line;
    while((line=scan.nextLine())!=null){
        System.out.println(line);
    }
    scan.close();
}

тогда выход будет:

the first line
the second line
Exception in thread "main" java.util.NoSuchElementException: No line found
    at java.util.Scanner.nextLine(Scanner.java:1540)
    at com.demo.Demo.testScan(Demo.java:39)
    at com.demo.Demo.main(Demo.java:49)

Ответ 1

Потому что он запрограммирован таким образом.

Действительно, это то, чего хочет пользователь метода. Если в последней строке отсутствует разделитель строк в конце, он будет читать до EOF, чтобы данные не были потеряны. Вы не хотите потерять целую строку из-за отсутствия разделителя строк.

Практически все подобные функции работают одинаково. Например, если вы смотрите на функцию fgets() в библиотеке C, она также будет работать именно так. Также f.readline() в Python.

Изменить: Сканер работает также аналогичным образом, но разница в том, что сканер генерирует исключение, тогда как BufferedReader возвращает значение null, когда все строки были прочитаны.

Ответ 2

казалось, что javadoc не говорит, что EOF также является разделителем строк в явном виде.

Я думаю, вы путаете разделитель строк с терминатором линии.

Разделитель строк просто отделяет линии друг от друга. Для разделителя строк ; и ввода one;two;three вы получите строки one, two и three. Однако, учитывая тот же символ и ввод, но ; является ограничителем строк, вы получите строки one и two, так как последняя строка не будет завершена.

На практике это означает, что если EOF действительно будет разделителем строк, вы получите дополнительные данные. Поскольку EOF технически не является символом, а условием, что файл закончился, наличие EOF в качестве разделителя строк будет иметь дикие последствия.

Однако, учитывая javadoc:

Считывает текст. Линия считается прекращенной любым ('\n'), возврат каретки ('\ r') или возврат каретки после чего сразу возвращается строка.

Я думаю, что терминология также используется неправильно. Либо javadoc должен говорить о разделении вместо прекращения, он должен упоминать EOF как одно из условий, заканчивающих линию, или реализация не должна рассматривать последний как отдельную строку.

Из Wikipedia:

Два способа просмотра строк новой строки, оба из которых являются самосогласованными, состоят в том, что новые линии либо отдельные строки, либо завершающие строки. Если newline считается разделителем, после последней строки файла. Некоторые программы имеют проблемы с обработкой последних строка файла, если она не завершена новой строкой. С другой программы, которые ожидают использования новой строки в качестве разделителя, будут интерпретировать окончательную новую строку как начало новой (пустой) линии. Наоборот, если новая строка считается терминатором, все текстовые строки, включая последние, как ожидается, будут завершены новой линией. Если окончательный символьная последовательность в текстовом файле не является новой строкой, последняя строка файл может считаться неправильной или неполной текстовой строкой, или файл может считаться неправильно усеченным.

Итак, похоже, что readLine() имеет эти смешения.

IMO readLine() javadoc должен сказать что-то вроде:

Линия считается завершенной в конце файла или по любому каналу ('\n'), возврат каретки ('\ r') или последующее возвращение каретки немедленно путем перевода строки.

или немного более смутное выражение, похожее на то, что Scanner.nextLine() говорит:

Этот метод возвращает текущую строку [..], за исключением любой строки разделитель в конце

С добавлением, что он вернет null, когда конец файла является единственным входом, остается.