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".