Reading a plain text file in Java
在Java中,读取和写入文件的数据似乎有不同的方式。
我想从一个文件中读取ASCII数据。可能的方法及其区别是什么?
我最喜欢的读取小文件的方法是使用BufferedReader和StringBuilder。它非常简单,而且切中要害(虽然不是特别有效,但对大多数情况来说已经足够好了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | BufferedReader br = new BufferedReader(new FileReader("file.txt")); try { StringBuilder sb = new StringBuilder(); String line = br.readLine(); while (line != null) { sb.append(line); sb.append(System.lineSeparator()); line = br.readLine(); } String everything = sb.toString(); } finally { br.close(); } |
有人指出,在Java 7之后,您应该使用试用资源(即自动关闭)功能:
1 2 3 4 5 6 7 8 9 10 11 | try(BufferedReader br = new BufferedReader(new FileReader("file.txt"))) { StringBuilder sb = new StringBuilder(); String line = br.readLine(); while (line != null) { sb.append(line); sb.append(System.lineSeparator()); line = br.readLine(); } String everything = sb.toString(); } |
当我读取这样的字符串时,我通常希望在任何情况下对每行进行一些字符串处理,所以接下来我将讨论这个实现。
不过,如果我真的想把一个文件读取到一个字符串中,我总是使用ApacheCommonsIO和ioutils.toString()类方法。您可以在这里查看源代码:
http://www.docjar.com/html/api/org/apache/commons/io/ioutils.java.html
1 2 3 4 5 6 | FileInputStream inputStream = new FileInputStream("foo.txt"); try { String everything = IOUtils.toString(inputStream); } finally { inputStream.close(); } |
甚至更简单的Java 7:
1 2 3 4 | try(FileInputStream inputStream = new FileInputStream("foo.txt")) { String everything = IOUtils.toString(inputStream); // do something with everything string } |
ascii是一个文本文件,因此您将使用
阅读本文,了解如何使用EDOCX1[4]
我也建议你下载并阅读这本叫做"爪哇思维"的书。
在Java 7中:
1 |
(DOCS)或
1 | Files.readAllLines(...) |
(DOCS)
在Java 8中:
1 | Files.lines(..).forEach(...) |
(DOCS)
最简单的方法是使用Java中的EDCOX1×5类和FieleRADER对象。简单例子:
1 |
例如,将整个内容读取到
1 2 3 4 5 6 | StringBuilder sb = new StringBuilder(); while(in.hasNext()) { sb.append(in.next()); } in.close(); outString = sb.toString(); |
另外,如果您需要特定的编码,您可以使用它而不是
1 |
下面是一个简单的解决方案:
以下是不使用外部库的另一种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import java.io.File; import java.io.FileReader; import java.io.IOException; public String readFile(String filename) { String content = null; File file = new File(filename); // For example, foo.txt FileReader reader = null; try { reader = new FileReader(file); char[] chars = new char[(int) file.length()]; reader.read(chars); content = new String(chars); reader.close(); } catch (IOException e) { e.printStackTrace(); } finally { if(reader != null){ reader.close(); } } return content; } |
我必须以不同的方式为基准。我将对我的发现发表评论,但简而言之,最快的方法是在fileinputstream上使用一个普通的旧bufferedinputstream。如果必须读取许多文件,那么三个线程会将总执行时间减少到大约一半,但是添加更多的线程会逐渐降低性能,直到20个线程的完成时间比一个线程的完成时间长三倍。
假设您必须读取一个文件并对其内容做一些有意义的事情。在这里的示例中,是从日志中读取行,并计算包含超过某个阈值的值的行。因此,我假设一个内衬Java 8 EDOCX1×0 }不是一个选项。
我在Java 1.8、Windows 7和SSD和HDD驱动器上进行了测试。
我写了六个不同的实现:
rawparse:在fileinputstream上使用bufferedinputstream,然后逐字节剪切读取行。这比任何其他单线程方法都好,但对于非ASCII文件来说可能非常不方便。
linereaderparse:在文件读取器上使用bufferedreader,逐行读取,通过调用string.split()拆分行。这大约比实际速度慢20%。
linereaderparseparallel:这与linereaderparse相同,但它使用多个线程。在所有情况下,这是最快的选择。
NoFielsPARSE:使用Java.Nio.Frask.Fix.LeScript()
NiAsyncParse:使用具有完成处理程序和线程池的异步文件通道。
NIOMemoryMappedParse:使用内存映射文件。这确实是一个坏主意,它产生的执行时间至少是任何其他实现的三倍。
这些是在四核i7和SSD驱动器上读取204个文件(每个文件大小为4 MB)的平均时间。这些文件是动态生成的,以避免磁盘缓存。
1 2 3 4 5 6 | rawParse 11.10 sec lineReaderParse 13.86 sec lineReaderParseParallel 6.00 sec nioFilesParse 13.52 sec nioAsyncParse 16.06 sec nioMemoryMappedParse 37.68 sec |
我发现运行在一个SSD或一个HDD驱动器上的差异比我预期的要小,因为它比SSD快大约15%。这可能是因为文件是在未分段的HDD上生成的,并且它们是按顺序读取的,因此旋转驱动器几乎可以像SSD一样执行。
我对nioasyncparse实现的低性能感到惊讶。或者我以错误的方式实现了某些东西,或者使用NIO和完成处理程序的多线程实现与使用java.io API的单线程实现执行相同(甚至更糟)。此外,与旧流上的直接实现相比,使用completionHandler的异步分析在代码行中要长得多,并且要正确实现要困难得多。
现在,六个实现后面跟着一个包含它们的类,再加上一个可参数化的main()方法,它允许播放文件数量、文件大小和并发度。请注意,这些文件的大小在正负20%之间变化。这是为了避免由于所有文件的大小完全相同而产生任何影响。
罗帕斯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | public void rawParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException { overrunCount = 0; final int dl = (int) ';'; StringBuffer lineBuffer = new StringBuffer(1024); for (int f=0; f<numberOfFiles; f++) { File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt"); FileInputStream fin = new FileInputStream(fl); BufferedInputStream bin = new BufferedInputStream(fin); int character; while((character=bin.read())!=-1) { if (character==dl) { // Here is where something is done with each line doSomethingWithRawLine(lineBuffer.toString()); lineBuffer.setLength(0); } else { lineBuffer.append((char) character); } } bin.close(); fin.close(); } } public final void doSomethingWithRawLine(String line) throws ParseException { // What to do for each line int fieldNumber = 0; final int len = line.length(); StringBuffer fieldBuffer = new StringBuffer(256); for (int charPos=0; charPos<len; charPos++) { char c = line.charAt(charPos); if (c==DL0) { String fieldValue = fieldBuffer.toString(); if (fieldValue.length()>0) { switch (fieldNumber) { case 0: Date dt = fmt.parse(fieldValue); fieldNumber++; break; case 1: double d = Double.parseDouble(fieldValue); fieldNumber++; break; case 2: int t = Integer.parseInt(fieldValue); fieldNumber++; break; case 3: if (fieldValue.equals("overrun")) overrunCount++; break; } } fieldBuffer.setLength(0); } else { fieldBuffer.append(c); } } } |
线段解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public void lineReaderParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException { String line; for (int f=0; f<numberOfFiles; f++) { File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt"); FileReader frd = new FileReader(fl); BufferedReader brd = new BufferedReader(frd); while ((line=brd.readLine())!=null) doSomethingWithLine(line); brd.close(); frd.close(); } } public final void doSomethingWithLine(String line) throws ParseException { // Example of what to do for each line String[] fields = line.split(";"); Date dt = fmt.parse(fields[0]); double d = Double.parseDouble(fields[1]); int t = Integer.parseInt(fields[2]); if (fields[3].equals("overrun")) overrunCount++; } |
线路读卡器面板面板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public void lineReaderParseParallel(final String targetDir, final int numberOfFiles, final int degreeOfParalelism) throws IOException, ParseException, InterruptedException { Thread[] pool = new Thread[degreeOfParalelism]; int batchSize = numberOfFiles / degreeOfParalelism; for (int b=0; b<degreeOfParalelism; b++) { pool[b] = new LineReaderParseThread(targetDir, b*batchSize, b*batchSize+b*batchSize); pool[b].start(); } for (int b=0; b<degreeOfParalelism; b++) pool[b].join(); } class LineReaderParseThread extends Thread { private String targetDir; private int fileFrom; private int fileTo; private DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private int overrunCounter = 0; public LineReaderParseThread(String targetDir, int fileFrom, int fileTo) { this.targetDir = targetDir; this.fileFrom = fileFrom; this.fileTo = fileTo; } private void doSomethingWithTheLine(String line) throws ParseException { String[] fields = line.split(DL); Date dt = fmt.parse(fields[0]); double d = Double.parseDouble(fields[1]); int t = Integer.parseInt(fields[2]); if (fields[3].equals("overrun")) overrunCounter++; } @Override public void run() { String line; for (int f=fileFrom; f<fileTo; f++) { File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt"); try { FileReader frd = new FileReader(fl); BufferedReader brd = new BufferedReader(frd); while ((line=brd.readLine())!=null) { doSomethingWithTheLine(line); } brd.close(); frd.close(); } catch (IOException | ParseException ioe) { } } } } |
NiFielSePARSE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public void nioFilesParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException { for (int f=0; f<numberOfFiles; f++) { Path ph = Paths.get(targetDir+filenamePreffix+String.valueOf(f)+".txt"); Consumer<String> action = new LineConsumer(); Stream<String> lines = Files.lines(ph); lines.forEach(action); lines.close(); } } class LineConsumer implements Consumer<String> { @Override public void accept(String line) { // What to do for each line String[] fields = line.split(DL); if (fields.length>1) { try { Date dt = fmt.parse(fields[0]); } catch (ParseException e) { } double d = Double.parseDouble(fields[1]); int t = Integer.parseInt(fields[2]); if (fields[3].equals("overrun")) overrunCount++; } } } |
NooAsiaCyPARSE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | public void nioAsyncParse(final String targetDir, final int numberOfFiles, final int numberOfThreads, final int bufferSize) throws IOException, ParseException, InterruptedException { ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(numberOfThreads); ConcurrentLinkedQueue<ByteBuffer> byteBuffers = new ConcurrentLinkedQueue<ByteBuffer>(); for (int b=0; b<numberOfThreads; b++) byteBuffers.add(ByteBuffer.allocate(bufferSize)); for (int f=0; f<numberOfFiles; f++) { consumerThreads.acquire(); String fileName = targetDir+filenamePreffix+String.valueOf(f)+".txt"; AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get(fileName), EnumSet.of(StandardOpenOption.READ), pool); BufferConsumer consumer = new BufferConsumer(byteBuffers, fileName, bufferSize); channel.read(consumer.buffer(), 0l, channel, consumer); } consumerThreads.acquire(numberOfThreads); } class BufferConsumer implements CompletionHandler<Integer, AsynchronousFileChannel> { private ConcurrentLinkedQueue<ByteBuffer> buffers; private ByteBuffer bytes; private String file; private StringBuffer chars; private int limit; private long position; private DateFormat frmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public BufferConsumer(ConcurrentLinkedQueue<ByteBuffer> byteBuffers, String fileName, int bufferSize) { buffers = byteBuffers; bytes = buffers.poll(); if (bytes==null) bytes = ByteBuffer.allocate(bufferSize); file = fileName; chars = new StringBuffer(bufferSize); frmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); limit = bufferSize; position = 0l; } public ByteBuffer buffer() { return bytes; } @Override public synchronized void completed(Integer result, AsynchronousFileChannel channel) { if (result!=-1) { bytes.flip(); final int len = bytes.limit(); int i = 0; try { for (i = 0; i < len; i++) { byte by = bytes.get(); if (by==' ') { // *** // The code used to process the line goes here chars.setLength(0); } else { chars.append((char) by); } } } catch (Exception x) { System.out.println( "Caught exception" + x.getClass().getName() +"" + x.getMessage() + " i=" + String.valueOf(i) +", limit=" + String.valueOf(len) + ", position="+String.valueOf(position)); } if (len==limit) { bytes.clear(); position += len; channel.read(bytes, position, channel, this); } else { try { channel.close(); } catch (IOException e) { } consumerThreads.release(); bytes.clear(); buffers.add(bytes); } } else { try { channel.close(); } catch (IOException e) { } consumerThreads.release(); bytes.clear(); buffers.add(bytes); } } @Override public void failed(Throwable e, AsynchronousFileChannel channel) { } }; |
所有案例的完全可运行实现
https://github.com/sergiomt/javaiobenchmark/blob/master/filereadbenchmark.java
以下是三种工作和测试方法:
使用1 2 3 4 5 6 7 8 9 10 11 12 | package io; import java.io.*; public class ReadFromFile2 { public static void main(String[] args)throws Exception { File file = new File("C:\\Users\\pankaj\\Desktop\\test.java"); BufferedReader br = new BufferedReader(new FileReader(file)); String st; while((st=br.readLine()) != null){ System.out.println(st); } } } |
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package io; import java.io.File; import java.util.Scanner; public class ReadFromFileUsingScanner { public static void main(String[] args) throws Exception { File file = new File("C:\\Users\\pankaj\\Desktop\\test.java"); Scanner sc = new Scanner(file); while(sc.hasNextLine()){ System.out.println(sc.nextLine()); } } } |
使用
1 2 3 4 5 6 7 8 9 10 11 12 | package io; import java.io.*; public class ReadingFromFile { public static void main(String[] args) throws Exception { FileReader fr = new FileReader("C:\\Users\\pankaj\\Desktop\\test.java"); int i; while ((i=fr.read()) != -1){ System.out.print((char) i); } } } |
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package io; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class ReadingEntireFileWithoutLoop { public static void main(String[] args) throws FileNotFoundException { File file = new File("C:\\Users\\pankaj\\Desktop\\test.java"); Scanner sc = new Scanner(file); sc.useDelimiter("\\Z"); System.out.println(sc.next()); } } |
1 2 3 4 5 |
您希望如何处理文本?文件是否足够小,可以放入内存?我会尽量找到最简单的方法来处理您需要的文件。fileutils库非常适合这种情况。
我记录了用Java读取文件的15种方法,然后用不同的文件大小对它们进行测试,从1和1到nbsp;这里有三种方法来实现这一点:
测试在Java 7, 8和9中工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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-10KB.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); } } } |
测试在Java 7, 8, 9中工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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-10KB.txt"; FileReader fileReader = new FileReader(fileName); try (BufferedReader bufferedReader = new BufferedReader(fileReader)) { String line; while((line = bufferedReader.readLine()) != null) { System.out.println(line); } } } } |
这是在Java 8和9中测试的,但是由于lambda表达式的要求,在Java 7中不能工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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-10KB.txt"; File file = new File(fileName); try (Stream linesStream = Files.lines(file.toPath())) { linesStream.forEach(line -> { System.out.println(line); }); } } } |
下面是用Java 8方式做的一个内衬。假设
1 | Files.lines(Paths.get("text.txt")).collect(Collectors.toList()); |
这基本上与JesusRamos的答案完全相同,除了使用文件而不是文件阅读器加上迭代来逐步浏览文件的内容。
1 2 3 4 5 6 7 8 |
…抛出
使用BufferedReader:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; BufferedReader br; try { br = new BufferedReader(new FileReader("/fileToRead.txt")); try { String x; while ( (x = br.readLine()) != null ) { // Printing out each line in the file System.out.println(x); } } catch (IOException e) { e.printStackTrace(); } } catch (FileNotFoundException e) { System.out.println(e); e.printStackTrace(); } |
可能没有缓冲I/O的速度快,但相当简洁:
1 2 3 4 | String content; try (Scanner scanner = new Scanner(textFile).useDelimiter("\\Z")) { content = scanner.next(); } |
从Java文件中读取数据最简单的方法是使用文件类来读取文件和扫描器类来读取文件的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public static void main(String args[])throws Exception { File f = new File("input.txt"); takeInputIn2DArray(f); } public static void takeInputIn2DArray(File f) throws Exception { Scanner s = new Scanner(f); int a[][] = new int[20][20]; for(int i=0; i<20; i++) { for(int j=0; j<20; j++) { a[i][j] = s.nextInt(); } } } |
PS:别忘了导入java. U.L.*,让扫描仪工作。
到目前为止,我还没有在其他答案中看到它被提到。但是如果"最好"意味着速度,那么新的Java I/O(NIO)可能提供最快的性能,但并不总是最容易为某人学习。
http://download.oracle.com/javase/tutorial/essential/io/file.html(下载.oracle.com/javase/tutorial/essential/io/file.html)
在实践中,缓冲流类的性能要高得多,以至于nio.2api包含专门返回这些流类的方法,部分原因是为了鼓励您始终在应用程序中使用缓冲流。
下面是一个例子:
1 2 3 4 5 6 7 8 9 | Path path = Paths.get("/myfolder/myfile.ext"); try (BufferedReader reader = Files.newBufferedReader(path)) { // Read from the stream String currentLine = null; while ((currentLine = reader.readLine()) != null) //do your code here } catch (IOException e) { // Handle file I/O exception... } |
您可以替换此代码
1 |
具有
1 |
我推荐这篇文章来学习Java NIO和IO的主要用途。
Guava为此提供了一个一行程序:
1 2 3 4 | import com.google.common.base.Charsets; import com.google.common.io.Files; String contents = Files.toString(filePath, Charsets.UTF_8); |
这可能不是问题的确切答案。这只是读取文件的另一种方式,在这里您没有显式指定Java代码中的文件路径,而是将其读取为命令行参数。
使用以下代码,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class InputReader{ public static void main(String[] args)throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String s=""; while((s=br.readLine())!=null){ System.out.println(s); } } } |
只需继续运行:
1 | java InputReader < input.txt |
这将读取
您还可以使您的
1 | java InputReader < input.txt > output.txt |
这将从
Cactoos提供了一个声明性的一行代码:
1 |
您可以使用readalllines和
它默认使用UTF-8编码,可以正确读取ASCII数据。
还可以使用readallbytes:
我认为readallbytes更快更精确,因为它不会用
。这取决于你的需要,哪一个是合适的。
对于基于JSF的Maven Web应用程序,只需使用Classloader和
将Apache Commons IO依赖项放入POM中:
1 2 3 4 5 | <dependency> <groupId>org.apache.commons</groupId> commons-io</artifactId> <version>1.3.2</version> </dependency> |
使用下面的代码读取它(例如,下面的代码在.json文件中读取):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | String metadata = null; FileInputStream inputStream; try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); inputStream = (FileInputStream) loader .getResourceAsStream("/metadata.json"); metadata = IOUtils.toString(inputStream); inputStream.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return metadata; |
您可以对文本文件、属性文件、XSD架构等执行相同的操作。
使用JAVA接吻,如果这是关于结构简单:
1 2 3 4 5 6 7 8 9 10 11 12 | import static kiss.API.*; class App { void run() { String line; try (Close in = inOpen("file.dat")) { while ((line = readLine()) != null) { println(line); } } } } |
1 2 3 4 | String fileName = 'yourFileFullNameWithPath'; File file = new File(fileName); // Creates a new file object for your file FileReader fr = new FileReader(file);// Creates a Reader that you can use to read the contents of a file read your file BufferedReader br = new BufferedReader(fr); //Reads text from a character-input stream, buffering characters so as to provide for the efficient reading of characters, arrays, and lines. |
上述一组行可以写成一行,如下所示:
1 |
添加到字符串生成器(如果文件很大,建议使用字符串生成器,否则使用普通字符串对象)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
对于非常大的文件,我编写的代码要快得多:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public String readDoc(File f) { String text =""; int read, N = 1024 * 1024; char[] buffer = new char[N]; try { FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); while(true) { read = br.read(buffer, 0, N); text += new String(buffer, 0, read); if(read < N) { break; } } } catch(Exception ex) { ex.printStackTrace(); } return text; } |