Прочитать файл java bufferedreader
Мне нужно читать большой текстовый файл размером около 5-6 ГБ построчно, используя Java.
Как я могу сделать это быстро?
@kamaci et. и др. Этот вопрос не должен быть помечен как дубликат. «Быстро прочитать последнюю строчку» не является альтернативой, и ее спорно ли «быстрый способ чтения текстового файла строка за строкой» есть. Самый быстрый способ сделать что-то не обязательно является обычным способом. Кроме того, ответы ниже включают код, а наиболее подходящая альтернатива, которую вы перечисляете, - нет. Этот вопрос полезен. В настоящее время это лучший результат поиска в Google по запросу "java read file by line". И, наконец, его устранение приводит к переполнению стека и обнаружению, что 1 в каждом 2 вопросе помечается для удаления. Несмотря на то, что я читал комментарии, утверждая, что близкая политика SO - отстой, SO остается в этом. Это такая ограниченная перспектива для разработчиков, чтобы хотеть избежать избыточности любой ценой! Просто пусть это будет! Крем поднимется до самого верха, а дерьмо само по себе опустится до самого дна. Несмотря на то, что вопрос, возможно, задавался ранее (какой вопрос не является ??), это не означает, что новый вопрос не сможет лучше его сформулировать, получить лучшие ответы, повысить рейтинг в поисковых системах и т. Д. Интересно, что это вопрос теперь «защищен» . Просто невероятно, как вопросы помечаются как дубликаты, просто читая заголовок.Распространенным примером является использование
Вы можете читать данные быстрее, если предположите, что кодировка символов отсутствует. например, ASCII-7, но это не будет иметь большого значения. Весьма вероятно, что то, что вы будете делать с данными, займет гораздо больше времени.
РЕДАКТИРОВАТЬ: менее распространенный шаблон, который позволяет избежать line утечки.
ОБНОВЛЕНИЕ: в Java 8 вы можете сделать
Как выглядит этот шаблон при правильной обработке исключений? Я отмечаю, что br.close () генерирует IOException, что кажется удивительным - что может произойти при закрытии файла, открытого для чтения, в любом случае? Конструктор FileReader может вызвать исключение FileNotFound. Если у меня есть файл 200 МБ, и он может читать со скоростью 90 МБ / с, то я ожидаю, что это займет3 с? Похоже, мое занятие заняло несколько минут с этим «медленным» способом чтения Я на SSD, поэтому скорость чтения не должна быть проблемой?
@JiewMeng Так что я подозреваю, что что-то еще, что вы делаете, требует времени. Можете ли вы попробовать просто прочитать строки файла и ничего больше. Почему бы и нет for(String line = br.readLine(); line != null; line = br.readLine()) , в Java 8 вы можете сделать, try( Stream<String> lines = Files.lines(. ) ) < for( String line : (Iterable<String>) lines::iterator ) < . >> что трудно не ненавидеть. @AleksandrDubinsky Проблема с замыканиями в Java 8 заключается в том, что она очень легко делает код более сложным для чтения (а также более медленным). Я вижу, что многие разработчики злоупотребляют им, потому что это «круто».Посмотрите на этот блог:
Мой файл 1,5 гигабайт, и невозможно прочитать файл, используя ваш ответ! @AboozarRajabi Конечно, это возможно. Этот код может читать любой текстовый файл. Отказался за некачественную ссылку. Тут совершенно бессмысленно DataInputStream , а не тот поток закрыт. Ничего плохого в Java Tutorial, и нет необходимости цитировать произвольный сторонний мусор в Интернете, как этот. Я бы отказался от комментариев, у вас есть 4 строки 100% избыточных комментариев для 6 строк кода.Размер буфера может быть указан, или размер по умолчанию может быть использован. Значение по умолчанию достаточно велико для большинства целей.
После выхода Java 8 (март 2014 года) вы сможете использовать потоки:
Печать всех строк в файле:
Используйте StandardCharsets.UTF_8 , используйте Stream<String> для краткости, и избегайте использования, forEach() и особенно forEachOrdered() если нет причины. Если бы я использовал forEach вместо forEachOrdered, строки могли бы быть напечатаны не по порядку, не так ли? @msayag, ты прав, нужно forEachOrdered для того чтобы выполнить по порядку. Имейте в виду, что в этом случае вы не сможете распараллелить поток, хотя я обнаружил, что распараллеливание не включается, если в файле нет тысяч строк.Вот пример с полной обработкой ошибок и поддержкой спецификации кодировки для pre-Java 7. В Java 7 вы можете использовать синтаксис try-with-resources, который делает код чище.
Если вы просто хотите использовать кодировку по умолчанию, вы можете пропустить InputStream и использовать FileReader.
Вот Groovy-версия с полной обработкой ошибок:
Как ByteArrayInputStream поступает строковый литерал с чтением большого текстового файла? абсолютно бесполезно закрывается. Нет нужды закрывать каждый поток. Если вы закроете любой из этих потоков, вы автоматически закроете все остальные потоки .В Java 8 вы можете сделать:
Некоторые примечания: поток, возвращаемый Files.lines (в отличие от большинства потоков), должен быть закрыт. По причинам, указанным здесь, я избегаю использования forEach() . Странный код (Iterable<String>) lines::iterator переводит поток в Iterable.
Отказ от реализации Iterable этого кода определенно уродлив, хотя и полезен. Это нуждается в броске (то есть (Iterable<String>) ), чтобы работать. Как я могу пропустить первую строку с этим методом? @qed for(String line : (Iterable<String>) lines.skip(1)::iterator) Если вы не собираетесь на самом деле использовать Stream функции, используя Files.newBufferedReader вместо Files.lines и повторяющегося вызова , readLine() пока null вместо того , чтобы использовать конструкции , как , (Iterable<String>) lines::iterator кажется, гораздо проще . Почему вы используете :: in lines :: iterator? Единственное использование, которое я знаю для :: - это упаковывать имя метода в лямбда-функцию. В течение параметра цикла после того, как : должен быть переменной , а вы получите некоторый метод лямбда с помощью ::Что вы можете сделать, это отсканировать весь текст с помощью сканера и проходить текст построчно. Конечно, вы должны импортировать следующее:
Сканер в основном сканирует весь текст. Цикл while используется для перемещения по всему тексту.
.hasNextLine() Функция булева , которая возвращает истину , если есть еще несколько строк в тексте. .nextLine() Функция дает Вам всю строку в виде строки , которые вы можете использовать, как вы хотите. Пытаться System.out.println(line) напечатать текст.
Примечание: .txt - это текст типа файла.
Должно ли объявление метода выглядеть вместо этого: «public static void readText throws FileNotFoundException () <´ Like:« public static void readText () throws FileNotFoundException <´ Это значительно медленнее, чем он BufferedReader.readLine() , и он попросил самый эффективный метод.FileReader не позволит вам указать кодировку, используйте InputStreamReader вместо этого, если вам нужно указать ее:
Если вы импортировали этот файл из Windows, он может иметь кодировку ANSI (Cp1252), поэтому вам необходимо указать кодировку.
Я задокументировал и протестировал 10 различных способов чтения файлов на Java, а затем сопоставил их друг с другом, заставив их читать в тестовых файлах от 1 КБ до 1 ГБ. Вот самые быстрые 3 метода чтения файлов для чтения тестового файла объемом 1 ГБ.
Обратите внимание, что при выполнении тестов производительности я ничего не выводил на консоль, поскольку это действительно замедляло бы тестирование. Я просто хотел проверить скорость чтения.
Протестировано в Java 7, 8, 9. В целом это был самый быстрый метод. Чтение файла объемом 1 ГБ всегда было менее 1 секунды.
Это было успешно протестировано в Java 8 и 9, но не будет работать в Java 7 из-за отсутствия поддержки лямбда-выражений. Чтение файла размером 1 ГБ заняло около 3,5 секунд, что ставит его на второе место после чтения больших файлов.
Проверено на работу в Java 7, 8, 9. Для считывания тестового файла объемом 1 ГБ потребовалось около 4,5 секунд.
1- BufferedReader
BufferedReader - это подкласс Reader, который используется для упрощения чтения текста из потоков ввода символов (character input stream) и повышения производительности программы.
- CharArrayReader
- FileReader
- FilterReader
- InputStreamReader
- LineNumberReader
- PipedReader
- PushbackReader
- Reader
- StringReader
Принцип работы BufferedReader выглядит следующим образом:
BufferedReader оборачивает внутри него объект Reader, который автоматически считывает данные из источника (например, файла) и сохраняет их в buffer (буфер) BufferedReader.
BufferedReader переопределяет (override) методы, которые наследуются от его родительского класса, такие как read(), read(char) и т.д, чтобы гарантировать, что они будут работать с данными из buffer, а не из источника (например, файла). Кроме этого, BufferedReader также предоставляет метод readLine() для чтения строки текста из buffer.
Так называемый "buffer", упомянутый выше, на самом деле является просто массивом символов. Объект Reader (из BufferedReader) автоматически считывает символы из источника для хранения в этом массиве.
BufferedReader считывает символы из массива buffer и освобождает позиции чтения. Освобожденные позиции будут использоваться для хранения вновь прочитанных символов из источника.
- Конструктор BufferedReader(Read,int) создает объект BufferedReader с массивом buffer, размер которого указан.
- Конструктор BufferedReader(Reader) создает объект BufferedReader с массивом размера buffer, который имеет размер по умолчанию (sz = 8192).
2- BufferedReader vs Scanner
В некоторых случаях вы можете использовать Scanner вместо BufferedReader. Ниже приведено сравнение между этими двумя классами
- BufferedReader синхронизирован (потокобезопасен - thread-safe), а Scanner - нет.
- Сканер может анализировать примитивные типы и строки с помощью регулярных выражений.
- BufferedReader позволяет указать размер buffer, в то время как Scanner имеет фиксированный размер buffer (1024).
- BufferedReader имеет размер buffer по умолчанию больше.
- Scanner скрывает IOException, в то время как BufferedReader заставляет нас обрабатывать его.
- BufferedReader обычно быстрее Scanner, потому что он только считывает данные без их синтаксического анализа.
3- Examples
Чтобы создать объект BufferedReader, нам нужно создать объект Reader для чтения данных из источника (например, из файла). Поскольку Reader - это абстрактный класс, поэтому вам нужно создать его из одного из его подклассов
Например: Создать BufferedReader с размером массива buffer 16384. Примечание: тип данных char Java - 2 bytes. Это означает, что размер этого buffer составляет 32786 bytes (32 KB).
Создать BufferedReader с размером массива buffer по умолчанию (8192), что эквивалентно 16384 bytes (16 KB).
Вместо создания BufferedReader из его конструктора, вы можете создать BufferedReader следующим образом:
Например: Использование метода readLine() из BufferedReader для чтения каждой строки текста.
Абстрактные классы Reader и Writer являются символьными потоками.
Абстрактный класс Reader
Класс Reader обеспечивает поддержку символьного потока чтения аналогично тому, как это делает InputStream, реализующий модель байтового потока ввода. Методы Reader схожи с теми, которые объявлены в составе InputStream.
Методы класса Reader
Метод | Описание |
---|---|
abstract void close() throws IOException | Закрытие ранее открытого потока чтения. Метод используется для высвобождения ресурсов, связанных с потоком (таких как дескрипторы открытых файлов). Любые попытки обращения к закрытому потоку приводят к выбрасыванию исключения типа IOException, но повторное закрытие потока эффектов не вызывает. |
void mark(int limit) | Размещение метки в текущей позиции входного потока |
boolean markSupported() | Функция проверки поддержки потоком методов mark() и reset() |
boolean ready() | Функция проверки готовности объекта для чтения данных, т.е. в нем существует хотя бы один доступный символ. Следует заметить, что результат, равный false, отнюдь не свидетельствует о том, что очередной вызов read приведет к возникновению блокировки, поскольку в промежутке между обращениями к ready и read в поток может поступить очередная порция данных. |
void reset() | Сброс указателя ввода в ранее установленную позицию метки. |
long skip(long count) | Функция пропуска указанного в параметре count количества символов ввода. Возвращает количество действительно пропущенных символов. Значение count не должно быть отрицательным. |
int read() throws lOException | Чтение одного символа данных в виде целого числа в диапазоне от 0 до 65535. Если символов, готовых для чтения, нет ввиду достижения конца потока, то возвращается значение -1. Метод выполняет блокировку до тех пор, пока имеются доступные для чтения символы, не достигнут конец потока либо не выброшено исключение. |
abstract int read(char[] buf, int offset, int count) throws lOException | Чтение символов и сохранение их в массиве buf типа char. Максимальное количество читаемых символов определяется значением count. Символы записываются в массив, начиная с смещение offset. Содержимое всех остальных элементов массива buf остается неизменным. Метод возвращает количество фактически считанных символов. Если достигнут конец потока и не считан ни один символ, возвращается -1. Если значение count равно нулю, чтение не производится и возвращается 0. Метод выполняет блокировку до тех пор, пока имеются доступные для чтения символы не достигнут конец потока либо не выброшено исключение. |
int read(char[] buf) throws IOException | Метод аналогичен предыдущему при условии, offset = 0, count = buf.length. |
При реализации Reader требуется, чтобы производный класс (наследник) обеспечил практическое воплощение варианта метода read, осуществляющего чтение данных в массив символов, и версии метода close. Во многих случаях, однако, производительность операций может быть улучшена за счет переопределения в производных классах и других методов.
Наследники класса Reader
- BufferedReader - буферизированный входной символьный поток; увеличивает производительность за счёт буферизации ввода;
- CharArrayReader - входной поток чтения символьного массива;
- FileReader - входной поток чтения содержимого файла; в конструкторе класса нужно указать либо путь к файлу, либо объект типа File;
- FilterReader - фильтрующий читатель;
- InputStreamReader - входной поток, транслирующий байты в символы;
- LineNumberReader - входной поток, подсчитывающий строки;
- PipedReader - входной канал;
- PushbackReader - входной поток, позволяющий возвращать символы обратно в поток;
- StringReader - входной поток, читающий из строки.
Класс BufferedReader
Широкораспространенный в использовании класс BufferedReader считывает текст из символьного потока ввода, буферизируя прочитанные символы. Использование буфера призвано увеличить производительность чтения данных из потока.
BufferedReader имеет следующие конструкторы :
Примеры BufferedReader
В качестве примера рассмотрим программу подсчета общего количества символов и числа пробелов в символьном потоке чтения:
Программе в качестве параметра передается имя файла в командной строке. Переменная in представляет символьный поток чтения. Если имя файла не задано, то используется стандартный поток ввода, System.in, после "вложения" его в объект типа InputStreamReader, который выполняет преобразование байтового потока ввода в символьный поток чтения. В противном случае создается объект типа FileReader, расширяющего класс Reader.
В цикле for подсчитывается общее количество символов в файле. Кроме этого, с использованием метода isWhitespace класса Character выявляются символы пробела и вычисляется их количество, а затем на экран выводится результат.
Абстрактный класс Writer
Абстрактный класс Writer обеспечивает поддержку символьного потока записи аналогично тому, как это делает OutputStream, реализующий модель байтового потока вывода. Многие методы Writer схожи с теми, которые объявлены в OutputStream; помимо этого, в классе Writer предусмотрены некоторые другие полезные версии метода write.
Методы класса Writer
Все методы класса вызывают исключение IOException при возникновении ошибки.
Классы, производные от Writer, должны реализовать вариант метода write, связанного с записью символов из части массива, а также методов close и flush. Все остальные методы Writer основаны на трех методах, названных выше. Это отличает Writer от класса OutputStream, в котором в качестве базового метода вывода предусмотрен вариант write, осуществляющий вывод одного байта, а для методов flush и close предложены реализации по умолчанию.
Как и в случае с классом Reader, производительность операций может быть улучшена за счет переопределения в производных классах и других методов.
Наследники класса Writer
- BufferedWriter - буферизированный выходной символьный поток; позволяет повысить производительность за счёт снижения количества операций физической записи в выходное устройство;
- CharArrayWriter - выходной поток, который пишет в символьный массив;
- FileWriter - выходной поток, пишущий в файл; в конструкторе можно определить вывод в конец файла. Создание объекта не зависит от наличия файла, он будет создан в случае необходимости. Если файл существует и он доступен только для чтения, то передаётся исключение IOException
- FilterWriter - фильтрующий писатель
- OutputStreamWriter -выходной поток, транслирующий байты в символы
- PipedWriter - выходной канал
- PrintWriter - выходной поток, включающий методы print() и println()
- StringWriter - выходной поток, пишущий в строку
Класс BufferedWriter
Широкораспространенный в использовании класс BufferedWriter записывает текст в поток, предварительно буферизируя записываемые символы, тем самым снижая количество обращений к физическому носителю для записи данных.
BufferedWriter имеет следующие конструкторы :
В качестве параметра out конструктор BufferedWriter принимает поток вывода, в который надо осуществить запись. Второй параметр size указывает на размер буфера.
кажется, есть разные способы чтения и записи данных из файлов в Java.
Я хочу прочитать данные ASCII из файла. Каковы возможные пути и их различия?
ASCII-это текстовый файл, поэтому вы будете использовать читателей для чтения. Java также поддерживает чтение из двоичного файла с помощью InputStreams. Если считываемые файлы огромны, вы захотите использовать командой bufferedreader на FileReader для улучшения производительности.
пройти в этой статье о том, как использовать Reader
Я также рекомендую вам скачать и прочитать эту замечательную (пока бесплатно) забронировать называется Мышление На Java
В Java 7:
В Java 8:
мой любимый способ чтения небольшого файла-использовать BufferedReader и StringBuilder. Это очень просто и по существу (хотя и не особенно эффективно, но достаточно хорошо для большинства случаев):
некоторые указали, что после Java 7 Вы должны использовать try-with-resources (т. е. автоматическое закрытие) особенности:
когда я читаю строки, как это, я обычно хочу сделать некоторую обработку строк в строке в любом случае, поэтому я иду на это реализация.
хотя, если я хочу на самом деле просто прочитать файл в строку, я всегда использую Apache Commons IO С классом IOUtils.метод toString. Вы можете посмотреть на источник здесь:
и еще проще с Java 7:
самый простой способ-использовать Scanner класс в Java и объект FileReader. Простой пример:
Scanner имеет несколько методов для чтения в строках, числах и т. д. Дополнительную информацию об этом можно найти на странице документации Java.
например, чтение всего содержимого в String :
кроме того, если вам нужна конкретная кодировка вы можете использовать это вместо FileReader :
вот простое решение:
вот еще один способ сделать это без использования внешних библиотек:
Я должен был проверить разные способы. Я прокомментирую свои выводы, но, короче говоря, самый быстрый способ-использовать простой старый BufferedInputStream над FileInputStream. Если необходимо прочитать много файлов, то три потока сократят общее время выполнения примерно до половины, но добавление большего количества потоков будет постепенно снижать производительность, пока не займет в три раза больше времени для завершения двадцати потоков, чем только с одним потоком.
предполагается, что вы должны прочитать файл и сделать что-то значимое с его содержанием. В примерах здесь читаются строки из журнала и подсчитываются те, которые содержат значения, превышающие определенный порог. Поэтому я предполагаю, что однострочный Java 8 Files.lines(Paths.get("/path/to/file.txt")).map(line -> line.split(";")) - это не вариант.
Я тестировал на Java 1.8, Windows 7 и SSD и HDD дисках.
Я написал шесть различных реализаций:
rawParse: используйте BufferedInputStream над FileInputStream, а затем вырезать строки чтения байт за байтом. Это превзошло любой другой однопоточный подход, но это может быть очень неудобно для файлов, отличных от ASCII.
lineReaderParse: используйте BufferedReader над FileReader, читайте строку за строкой, разделяйте строки, вызывая строку.расщеплять.)( Это примерно на 20% медленнее, чем rawParse.
lineReaderParseParallel: это то же самое, что lineReaderParse, но он использует несколько потоков. Это самый быстрый вариант в целом случаи.
nioFilesParse: используйте java.НИО.файлы.Файлы.lines ()
nioAsyncParse: используйте асинхронный канал с обработчиком завершения и пулом потоков.
nioMemoryMappedParse: используйте файл, сопоставленный с памятью. Это действительно плохая идея, дающая время выполнения по крайней мере в три раза больше, чем любая другая реализация.
это среднее время для чтения 204 файлов по 4 МБ каждый на четырехъядерный i7 и SSD-накопитель. Файлы создаются на лету, чтобы избежать кэширования диска.
Я нашел разницу меньше, чем я ожидал, между запуском на SSD или жестком диске SSD примерно на 15% быстрее. Это может быть связано с тем, что файлы генерируются на нефрагментированном HDD, и они читаются последовательно, поэтому вращающийся диск может работать почти как SSD.
Я был удивлен низкой производительностью реализации nioAsyncParse. Любой Я реализовал что-то неправильно или многопоточная реализация с использованием NIO, а обработчик завершения выполняет то же самое (или даже хуже), что и однопоточная реализация с java.IO API. Кроме того, асинхронный синтаксический анализ с CompletionHandler намного длиннее в строках кода и сложнее реализовать правильно, чем прямая реализация на старых потоках.
теперь шесть реализаций, за которыми следует класс, содержащий их все, плюс параметризуемый метод main() это позволяет играть с количеством файлов, размером файла и степенью параллелизма. Обратите внимание, что размер файлов варьируется плюс минус 20%. Это позволяет избежать какого-либо эффекта из-за того, что все файлы одинаковы размер.
Читайте также: