Java чтение файла различными методами

Кажется, существует много способов чтения текстовых файлов на Java (BufferedReader, DataInputStream и т.д.). Мой личный фаворит Scanner с File в конструкторе (он просто проще, работает с улучшенной обработкой данных mathy и имеет знакомый синтаксис).

Борис Паук также упомянул Channel и RandomAccessFile.

Может ли кто-нибудь объяснить плюсы и минусы каждого из этих методов? Чтобы быть конкретным, когда я хочу использовать каждый?

(edit) Я думаю, что должен быть конкретным и добавить, что у меня есть сильное предпочтение методу Scanner. Итак, реальный вопрос: когда я не хочу его использовать?

Ответ 1

Давайте начнем с начала. Вопрос в том, что вы хотите сделать?

Важно понять, что такое файл на самом деле. Файл представляет собой набор байтов на диске, эти байты - ваши данные. Существуют различные уровни абстракции выше, чем Java:

  • File(Input|Output)Stream - прочитайте эти байты как поток byte.
  • File(Reader|Writer) - читать из потока байтов как поток char.
  • Scanner - прочитайте из потока char и отметьте его.
  • RandomAccessFile - прочитайте эти байты как доступные для поиска byte[].
  • FileChannel - прочитайте эти байты безопасным многопоточным способом.

В верхней части каждого из них есть Decorators, например, вы можете добавить буферизацию с помощью BufferedXXX. Вы можете добавить осведомленность о развязке в FileWriter с помощью PrintWriter. Вы можете включить InputStream в Reader с помощью InputStreamReader (в настоящее время это единственный способ указать кодировку символов для Reader).

Итак - когда я не хочу использовать его [a Scanner]?.

Вы бы не использовали Scanner, если хотите, (это несколько примеров):

  • Чтение данных как byte s
  • Чтение в сериализованном объекте Java
  • Скопируйте byte из одного файла в другой, возможно, с некоторой фильтрацией.

Также не стоит ничего, что конструктор Scanner(File file) принимает File и открывает FileInputStream с платформой по умолчанию - это почти всегда плохой идея. Общепризнано, что вы должны явно указать кодировку, чтобы избежать неприятных ошибок на основе кодировки. Далее поток не буферизуется.

Итак, вам может быть лучше с

try (final Scanner scanner = new Scanner(new BufferedInputStream(new FileInputStream())), "UTF-8") {
    //do stuff
}

Уродливо, я знаю.

Стоит отметить, что Java 7 Предоставляет дополнительный уровень абстракции для удаления необходимости перебирать файлы - они находятся в классе Files:

byte[] Files.readAllBytes(Path path)
List<String> Files.readAllLines(Path path, Charset cs)

Оба эти метода считывают весь файл в память, что может оказаться неприемлемым. В Java 8 это улучшилось, добавив поддержку нового API Stream:

Stream<String> Files.lines(Path path, Charset cs)
Stream<Path> Files.list(Path dir)

Например, чтобы получить Stream слова из Path, вы можете сделать:

    final Stream<String> words = Files.lines(Paths.get("myFile.txt")).
            flatMap((in) -> Arrays.stream(in.split("\\b")));

Ответ 2

SCANNER:

может анализировать примитивные типы и строки с использованием регулярных выражений. Сканер разбивает свой ввод на токены, используя шаблон разделителя, который по умолчанию соответствует пробелу. Затем полученные маркеры могут быть преобразованы в значения разных типов. Более подробно можно прочитать на http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html

DATA INPUT STREAM:

Позволяет приложению читать примитивные типы данных Java из базового потока ввода машинно-независимым способом. Приложение использует выходной поток данных для записи данных, которые впоследствии могут быть прочитаны потоком ввода данных. DataInputStream не обязательно безопасен для многопоточного доступа. Безопасность потоков является необязательной и отвечает за использование методов в этом классе. Подробнее можно прочитать на http://docs.oracle.com/javase/7/docs/api/java/io/DataInputStream.html

BufferedReader:

Читает текст из потока ввода символов, буферизуя символы, чтобы обеспечить эффективное считывание символов, массивов и строк. Можно указать размер буфера или использовать размер по умолчанию. Значение по умолчанию достаточно велико для большинства целей. В общем, каждый запрос на чтение, сделанный из Reader, вызывает необходимость в соответствующем запросе на чтение базового символа или байтового потока. Поэтому целесообразно обернуть BufferedReader вокруг любого Reader, чьи операции read() могут быть дорогостоящими, например FileReaders и InputStreamReaders. Например,

BufferedReader in   = new BufferedReader(new FileReader("foo.in"));

будет буферизовать ввод из указанного файла. Без буферизации каждый вызов read() или readLine() может привести к чтению байтов из файла, преобразованию в символы, а затем возврату, что может быть очень неэффективным. DataInputStream с соответствующим BufferedReader.Более подробно на http://docs.oracle.com/javase/7/docs/api/java/io/BufferedReader.html

Ответ 3

ПРИМЕЧАНИЕ. Этот подход устарел. Как отмечает Борис в своем комментарии. Я оставлю его здесь для истории, но вы должны использовать методы, доступные в JDK.

Это зависит от того, какую операцию вы делаете, и размера файла, который вы читаете.

В большинстве случаев я рекомендую использовать commons-io для небольших файлов.

byte[] data = FileUtils.readFileToByteArray(new File("myfile"));

Вы можете прочитать его как строку или массив символов...

Теперь вы передаете большие файлы или меняете части файла непосредственно в файловой системе, тогда лучше всего использовать RandomAccessFile и потенциально даже FileChannel для создания стиля "nio".