Tôi cần đọc một tệp văn bản lớn khoảng 5-6 GB theo từng dòng bằng Java.
Làm thế nào tôi có thể làm điều này một cách nhanh chóng?
Tôi cần đọc một tệp văn bản lớn khoảng 5-6 GB theo từng dòng bằng Java.
Làm thế nào tôi có thể làm điều này một cách nhanh chóng?
Câu trả lời:
Một mô hình phổ biến là sử dụng
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
while ((line = br.readLine()) != null) {
// process the line.
}
}
Bạn có thể đọc dữ liệu nhanh hơn nếu bạn cho rằng không có mã hóa ký tự. ví dụ ASCII-7 nhưng nó sẽ không tạo ra nhiều khác biệt. Rất có khả năng những gì bạn làm với dữ liệu sẽ mất nhiều thời gian hơn.
EDIT: Một mô hình ít phổ biến hơn để sử dụng để tránh phạm vi line
rò rỉ.
try(BufferedReader br = new BufferedReader(new FileReader(file))) {
for(String line; (line = br.readLine()) != null; ) {
// process the line.
}
// line is not visible here.
}
CẬP NHẬT: Trong Java 8 bạn có thể làm
try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
stream.forEach(System.out::println);
}
LƯU Ý: Bạn phải đặt Luồng trong khối thử tài nguyên để đảm bảo phương thức #close được gọi trên đó, nếu không, phần xử lý tệp bên dưới sẽ không bao giờ bị đóng cho đến khi GC thực hiện sau đó.
for(String line = br.readLine(); line != null; line = br.readLine())
Btw, trong Java 8 bạn có thể làm try( Stream<String> lines = Files.lines(...) ){ for( String line : (Iterable<String>) lines::iterator ) { ... } }
Điều đó thật khó để không ghét.
Nhìn vào blog này:
Kích thước bộ đệm có thể được chỉ định hoặc kích thước mặc định có thể được sử dụng. Mặc định là đủ lớn cho hầu hết các mục đích.
// Open the file
FileInputStream fstream = new FileInputStream("textfile.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
String strLine;
//Read File Line By Line
while ((strLine = br.readLine()) != null) {
// Print the content on the console
System.out.println (strLine);
}
//Close the input stream
fstream.close();
DataInputStream
, và luồng sai được đóng lại. Không có gì sai với Hướng dẫn Java và không cần trích dẫn rác Internet của bên thứ ba tùy tiện như thế này.
Khi Java 8 ra mắt (tháng 3 năm 2014), bạn sẽ có thể sử dụng các luồng:
try (Stream<String> lines = Files.lines(Paths.get(filename), Charset.defaultCharset())) {
lines.forEachOrdered(line -> process(line));
}
In tất cả các dòng trong tệp:
try (Stream<String> lines = Files.lines(file, Charset.defaultCharset())) {
lines.forEachOrdered(System.out::println);
}
StandardCharsets.UTF_8
, sử dụng Stream<String>
cho sự thống nhất và tránh sử dụng forEach()
và đặc biệt forEachOrdered()
trừ khi có lý do.
forEach(this::process)
, nhưng sẽ trở nên xấu xí nếu bạn viết các khối mã như lambdas bên trong forEach()
.
forEachOrdered
để thực hiện theo thứ tự. Xin lưu ý rằng bạn sẽ không thể song song hóa luồng trong trường hợp đó, mặc dù tôi đã thấy rằng song song hóa không bật trừ khi tệp có hàng ngàn dòng.
Dưới đây là một mẫu với xử lý lỗi đầy đủ và đặc tả bộ ký tự hỗ trợ cho tiền Java 7. Với Java 7, bạn có thể sử dụng cú pháp try-with-resource, giúp mã sạch hơn.
Nếu bạn chỉ muốn bộ ký tự mặc định, bạn có thể bỏ qua InputStream và sử dụng FileReader.
InputStream ins = null; // raw byte-stream
Reader r = null; // cooked reader
BufferedReader br = null; // buffered for readLine()
try {
String s;
ins = new FileInputStream("textfile.txt");
r = new InputStreamReader(ins, "UTF-8"); // leave charset out for default
br = new BufferedReader(r);
while ((s = br.readLine()) != null) {
System.out.println(s);
}
}
catch (Exception e)
{
System.err.println(e.getMessage()); // handle exception
}
finally {
if (br != null) { try { br.close(); } catch(Throwable t) { /* ensure close happens */ } }
if (r != null) { try { r.close(); } catch(Throwable t) { /* ensure close happens */ } }
if (ins != null) { try { ins.close(); } catch(Throwable t) { /* ensure close happens */ } }
}
Đây là phiên bản Groovy, với xử lý lỗi đầy đủ:
File f = new File("textfile.txt");
f.withReader("UTF-8") { br ->
br.eachLine { line ->
println line;
}
}
ByteArrayInputStream
được cung cấp bởi một chuỗi chữ có liên quan đến việc đọc một tệp văn bản lớn?
Trong Java 8, bạn có thể làm:
try (Stream<String> lines = Files.lines (file, StandardCharsets.UTF_8))
{
for (String line : (Iterable<String>) lines::iterator)
{
;
}
}
Một số lưu ý: Luồng được trả về bởi Files.lines
(không giống như hầu hết các luồng) cần phải được đóng lại. Vì những lý do được đề cập ở đây tôi tránh sử dụng forEach()
. Mã lạ (Iterable<String>) lines::iterator
đưa một luồng đến một vòng lặp.
Iterable
mã này chắc chắn là xấu mặc dù hữu ích. Nó cần một diễn viên (tức là (Iterable<String>)
) để làm việc.
for(String line : (Iterable<String>) lines.skip(1)::iterator)
Stream
các tính năng, sử dụng Files.newBufferedReader
thay vì Files.lines
và liên tục gọi readLine()
cho đến khi null
thay vì sử dụng các cấu trúc như (Iterable<String>) lines::iterator
có vẻ đơn giản hơn nhiều
Những gì bạn có thể làm là quét toàn bộ văn bản bằng Máy quét và đi qua từng dòng văn bản. Tất nhiên bạn nên nhập như sau:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public static void readText throws FileNotFoundException {
Scanner scan = new Scanner(new File("samplefilename.txt"));
while(scan.hasNextLine()){
String line = scan.nextLine();
//Here you can manipulate the string the way you want
}
}
Máy quét về cơ bản quét tất cả các văn bản. Vòng lặp while được sử dụng để duyệt qua toàn bộ văn bản.
Các .hasNextLine()
chức năng là một boolean mà trả về true nếu vẫn còn nhiều dòng trong văn bản. Các .nextLine()
chức năng cung cấp cho bạn toàn bộ một dòng như là một String mà sau đó bạn có thể sử dụng theo cách bạn muốn. Cố gắng System.out.println(line)
in văn bản.
Lưu ý bên: .txt là văn bản loại tệp.
BufferedReader.readLine()
, và ông đã yêu cầu phương pháp thực hiện tốt nhất.
FileReader sẽ không cho phép bạn chỉ định mã hóa, InputStreamReader
thay vào đó hãy sử dụng nếu bạn cần chỉ định nó:
try {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "Cp1252"));
String line;
while ((line = br.readLine()) != null) {
// process the line.
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
Nếu bạn đã nhập tệp này từ Windows, nó có thể có mã hóa ANSI (Cp1252), vì vậy bạn phải chỉ định mã hóa.
Tôi đã ghi lại và thử nghiệm 10 cách khác nhau để đọc một tệp trong Java và sau đó chạy chúng với nhau bằng cách làm cho chúng đọc trong các tệp thử nghiệm từ 1KB đến 1GB. Dưới đây là 3 phương pháp đọc tệp nhanh nhất để đọc tệp kiểm tra 1GB.
Lưu ý rằng khi chạy các bài kiểm tra hiệu năng, tôi không xuất bất kỳ thứ gì lên bàn điều khiển vì điều đó thực sự sẽ làm chậm bài kiểm tra. Tôi chỉ muốn kiểm tra tốc độ đọc thô.
1) java.nio.file.Files.read ALLBytes ()
Đã thử nghiệm trong Java 7, 8, 9. Đây là phương pháp nhanh nhất. Đọc tệp 1GB liên tục chỉ dưới 1 giây.
import java.io..File;
import java.io.IOException;
import java.nio.file.Files;
public class ReadFile_Files_ReadAllBytes {
public static void main(String [] pArgs) throws IOException {
String fileName = "c:\\temp\\sample-1GB.txt";
File file = new File(fileName);
byte [] fileBytes = Files.readAllBytes(file.toPath());
char singleChar;
for(byte b : fileBytes) {
singleChar = (char) b;
System.out.print(singleChar);
}
}
}
2) java.nio.file.Files.lines ()
Điều này đã được thử nghiệm thành công trong Java 8 và 9 nhưng nó sẽ không hoạt động trong Java 7 vì thiếu sự hỗ trợ cho các biểu thức lambda. Mất khoảng 3,5 giây để đọc trong tệp 1GB, đặt nó ở vị trí thứ hai xa như đọc các tệp lớn hơn.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.stream.Stream;
public class ReadFile_Files_Lines {
public static void main(String[] pArgs) throws IOException {
String fileName = "c:\\temp\\sample-1GB.txt";
File file = new File(fileName);
try (Stream linesStream = Files.lines(file.toPath())) {
linesStream.forEach(line -> {
System.out.println(line);
});
}
}
}
3) Bộ đệm
Đã thử nghiệm để hoạt động trong Java 7, 8, 9. Điều này mất khoảng 4,5 giây để đọc trong tệp thử nghiệm 1GB.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFile_BufferedReader_ReadLine {
public static void main(String [] args) throws IOException {
String fileName = "c:\\temp\\sample-1GB.txt";
FileReader fileReader = new FileReader(fileName);
try (BufferedReader bufferedReader = new BufferedReader(fileReader)) {
String line;
while((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
}
}
Bạn có thể tìm thấy bảng xếp hạng hoàn chỉnh cho tất cả 10 phương pháp đọc tệp tại đây .
System.out.print/println()
ở đây; bạn cũng cho rằng tập tin sẽ vừa với bộ nhớ trong hai trường hợp đầu tiên của bạn.
Trong Java 7:
String folderPath = "C:/folderOfMyFile";
Path path = Paths.get(folderPath, "myFileName.csv"); //or any text file eg.: txt, bat, etc
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(path , charset)) {
while ((line = reader.readLine()) != null ) {
//separate all csv fields into string array
String[] lineVariables = line.split(",");
}
} catch (IOException e) {
System.err.println(e);
}
StandardCharsets.UTF_8
để tránh ngoại lệ được kiểm tra trongCharset.forName("UTF-8")
Trong Java 8, cũng có một cách khác để sử dụng Files.lines()
. Nếu nguồn đầu vào của bạn không phải là một tệp mà là một thứ gì đó trừu tượng hơn như một Reader
hoặc một InputStream
, bạn có thể truyền phát các dòng thông qua phương thức BufferedReader
s lines()
.
Ví dụ:
try (BufferedReader reader = new BufferedReader(...)) {
reader.lines().forEach(line -> processLine(line));
}
sẽ gọi processLine()
cho mỗi dòng đầu vào được đọc bởi BufferedReader
.
Để đọc một tệp với Java 8
package com.java.java8;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
/**
* The Class ReadLargeFile.
*
* @author Ankit Sood Apr 20, 2017
*/
public class ReadLargeFile {
/**
* The main method.
*
* @param args
* the arguments
*/
public static void main(String[] args) {
try {
Stream<String> stream = Files.lines(Paths.get("C:\\Users\\System\\Desktop\\demoData.txt"));
stream.forEach(System.out::println);
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Bạn có thể sử dụng lớp Scanner
Scanner sc=new Scanner(file);
sc.nextLine();
Scanner
là tốt, nhưng câu trả lời này không bao gồm mã đầy đủ để sử dụng nó đúng cách.
BufferedReader.readLine()
chắc chắn là nhanh gấp nhiều lần. Nếu bạn nghĩ khác xin vui lòng cung cấp lý do của bạn.
Bạn cần sử dụng readLine()
phương pháp trong class BufferedReader
. Tạo một đối tượng mới từ lớp đó và vận hành phương thức này trên anh ta và lưu nó vào một chuỗi.
Cách rõ ràng để đạt được điều này,
Ví dụ:
Nếu bạn có dataFile.txt
trong thư mục hiện tại của bạn
import java.io.*;
import java.util.Scanner;
import java.io.FileNotFoundException;
public class readByLine
{
public readByLine() throws FileNotFoundException
{
Scanner linReader = new Scanner(new File("dataFile.txt"));
while (linReader.hasNext())
{
String line = linReader.nextLine();
System.out.println(line);
}
linReader.close();
}
public static void main(String args[]) throws FileNotFoundException
{
new readByLine();
}
}
try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
stream.forEach(System.out::println);
}
System.getProperty("os.name").equals("Linux")
==
!
BufferedReader br;
FileInputStream fin;
try {
fin = new FileInputStream(fileName);
br = new BufferedReader(new InputStreamReader(fin));
/*Path pathToFile = Paths.get(fileName);
br = Files.newBufferedReader(pathToFile,StandardCharsets.US_ASCII);*/
String line = br.readLine();
while (line != null) {
String[] attributes = line.split(",");
Movie movie = createMovie(attributes);
movies.add(movie);
line = br.readLine();
}
fin.close();
br.close();
} catch (FileNotFoundException e) {
System.out.println("Your Message");
} catch (IOException e) {
System.out.println("Your Message");
}
Nó làm việc cho tôi. Hy vọng nó sẽ giúp bạn quá.
Bạn có thể sử dụng các luồng để làm điều đó chính xác hơn:
Files.lines(Paths.get("input.txt")).forEach(s -> stringBuffer.append(s);
Tôi thường làm các thói quen đọc đơn giản:
void readResource(InputStream source) throws IOException {
BufferedReader stream = null;
try {
stream = new BufferedReader(new InputStreamReader(source));
while (true) {
String line = stream.readLine();
if(line == null) {
break;
}
//process line
System.out.println(line)
}
} finally {
closeQuiet(stream);
}
}
static void closeQuiet(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException ignore) {
}
}
}
Bạn có thể sử dụng mã này:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class ReadTextFile {
public static void main(String[] args) throws IOException {
try {
File f = new File("src/com/data.txt");
BufferedReader b = new BufferedReader(new FileReader(f));
String readLine = "";
System.out.println("Reading file using Buffered Reader");
while ((readLine = b.readLine()) != null) {
System.out.println(readLine);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Bằng cách sử dụng gói org.apache.commons.io , nó mang lại hiệu năng cao hơn, đặc biệt là trong mã kế thừa sử dụng Java 6 trở xuống.
Java 7 có API tốt hơn với ít xử lý ngoại lệ hơn và các phương thức hữu ích hơn:
LineIterator lineIterator = null;
try {
lineIterator = FileUtils.lineIterator(new File("/home/username/m.log"), "windows-1256"); // The second parameter is optionnal
while (lineIterator.hasNext()) {
String currentLine = lineIterator.next();
// Some operation
}
}
finally {
LineIterator.closeQuietly(lineIterator);
}
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
Bạn cũng có thể sử dụng Apache Commons IO :
File file = new File("/home/user/file.txt");
try {
List<String> lines = FileUtils.readLines(file);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
FileUtils.readLines(file)
là một phương pháp không dùng nữa. Ngoài ra, phương thức gọi IOUtils.readLines
, sử dụng BufferedReader và ArrayList. Đây không phải là một phương pháp theo từng dòng và chắc chắn không phải là một phương pháp thực tế để đọc vài GB.