使用 java nio 将字符串写入文件的最佳方法
我需要使用 java nio 将巨大的字符串写入(附加)到平面文件。编码为 ISO-8859-1。
目前我们正在编写如下所示。有没有更好的方法来做同样的事情?
public void writeToFile(Long limit) throws IOException{
String fileName = "/xyz/test.txt";
File file = new File(fileName);
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
FileChannel fileChannel = fileOutputStream.getChannel();
ByteBuffer byteBuffer = null;
String messageToWrite = null;
for(int i=1; i<limit; i++){
//messageToWrite = get String Data From database
byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset.forName("ISO-8859-1")));
fileChannel.write(byteBuffer);
}
fileChannel.close();
}
编辑:尝试了两个选项。以下是结果。
@Test
public void testWritingStringToFile() {
DiagnosticLogControlManagerImpl diagnosticLogControlManagerImpl = new DiagnosticLogControlManagerImpl();
try {
File file = diagnosticLogControlManagerImpl.createFile();
long startTime = System.currentTimeMillis();
writeToFileNIOWay(file);
//writeToFileIOWay(file);
long endTime = System.currentTimeMillis();
System.out.println("Total Time is " + (endTime - startTime));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
*
* @param limit
* Long
* @throws IOException
* IOException
*/
public void writeToFileNIOWay(File file) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
FileChannel fileChannel = fileOutputStream.getChannel();
ByteBuffer byteBuffer = null;
String messageToWrite = null;
for (int i = 1; i < 1000000; i++) {
messageToWrite = "This is a test üüüüüüööööö";
byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset
.forName("ISO-8859-1")));
fileChannel.write(byteBuffer);
}
}
/**
*
* @param limit
* Long
* @throws IOException
* IOException
*/
public void writeToFileIOWay(File file) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
fileOutputStream, 128 * 100);
String messageToWrite = null;
for (int i = 1; i < 1000000; i++) {
messageToWrite = "This is a test üüüüüüööööö";
bufferedOutputStream.write(messageToWrite.getBytes(Charset
.forName("ISO-8859-1")));
}
bufferedOutputStream.flush();
fileOutputStream.close();
}
private File createFile() throws IOException {
File file = new File(FILE_PATH + "test_sixth_one.txt");
file.createNewFile();
return file;
}
使用 ByteBuffer 和 Channel:花费了 4402 毫秒
使用缓冲写入器:花费了 563 毫秒
I need to write(append) huge string to flat file using java nio. The encoding is ISO-8859-1.
Currently we are writing as shown below. Is there any better way to do the same ?
public void writeToFile(Long limit) throws IOException{
String fileName = "/xyz/test.txt";
File file = new File(fileName);
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
FileChannel fileChannel = fileOutputStream.getChannel();
ByteBuffer byteBuffer = null;
String messageToWrite = null;
for(int i=1; i<limit; i++){
//messageToWrite = get String Data From database
byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset.forName("ISO-8859-1")));
fileChannel.write(byteBuffer);
}
fileChannel.close();
}
EDIT: Tried both options. Following are the results.
@Test
public void testWritingStringToFile() {
DiagnosticLogControlManagerImpl diagnosticLogControlManagerImpl = new DiagnosticLogControlManagerImpl();
try {
File file = diagnosticLogControlManagerImpl.createFile();
long startTime = System.currentTimeMillis();
writeToFileNIOWay(file);
//writeToFileIOWay(file);
long endTime = System.currentTimeMillis();
System.out.println("Total Time is " + (endTime - startTime));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
*
* @param limit
* Long
* @throws IOException
* IOException
*/
public void writeToFileNIOWay(File file) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
FileChannel fileChannel = fileOutputStream.getChannel();
ByteBuffer byteBuffer = null;
String messageToWrite = null;
for (int i = 1; i < 1000000; i++) {
messageToWrite = "This is a test üüüüüüööööö";
byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset
.forName("ISO-8859-1")));
fileChannel.write(byteBuffer);
}
}
/**
*
* @param limit
* Long
* @throws IOException
* IOException
*/
public void writeToFileIOWay(File file) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
fileOutputStream, 128 * 100);
String messageToWrite = null;
for (int i = 1; i < 1000000; i++) {
messageToWrite = "This is a test üüüüüüööööö";
bufferedOutputStream.write(messageToWrite.getBytes(Charset
.forName("ISO-8859-1")));
}
bufferedOutputStream.flush();
fileOutputStream.close();
}
private File createFile() throws IOException {
File file = new File(FILE_PATH + "test_sixth_one.txt");
file.createNewFile();
return file;
}
Using ByteBuffer and Channel: took 4402 ms
Using buffered Writer : Took 563 ms
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
更新:
从Java11开始,有一种使用
java.nio.file.Files
编写字符串的特定方法:我们还可以使用以下方式自定义编写:
原始答案:
有一个单行解决方案,使用Java nio:
我还没有与其他解决方案进行基准测试,但是使用打开-写入-关闭文件的内置实现应该是快速和代码相当小。
UPDATED:
Since Java11 there is a specific method to write strings using
java.nio.file.Files
:We can also customize the writing with:
ORIGINAL ANSWER:
There is a one-line solution, using Java nio:
I have not benchmarked this solution with the others, but using the built-in implementation for open-write-close file should be fast and the code is quite small.
我认为如果不对您的软件进行基准测试,您将无法得到严格的答案。在适当的条件下,NIO 可能会显着加快应用程序的速度,但也可能会使速度变慢。
这里有一些要点:
倒带
和翻转
吗?似乎您正在为每个字符串创建一个新缓冲区并将其写入通道。 (如果你采用 NIO 方式,即重用缓冲区而不是包装/丢弃的基准策略,我认为它们会做得更好)。wrap
和 allocateDirect 可能会产生完全不同的缓冲区。基准测试两者要把握权衡。使用直接分配时,请确保重复使用相同的缓冲区以获得最佳性能。byte[]
或char[]
缓冲区)。我见过很多,许多,很多人发现蔚来并不是灵丹妙药。如果您喜欢一些前沿...返回IO Trails对于一些 NIO2 :D。
这是一个关于使用不同文件复制的有趣的基准策略。我知道这是一个不同的问题,但我认为大多数事实和作者的结论也适用于您的问题。
干杯,
更新 1:
由于 @EJP 告诉我直接缓冲区对于这个问题效率不高,所以我自己对其进行了基准测试,并最终得到了一个使用内存映射文件的不错的 NIO 解决方案。在我运行 OS X Lion 的 Macbook 中,这明显优于 BufferedOutputStream。但请记住,这可能是操作系统/硬件/虚拟机特定的:
我承认我通过预先计算要附加的总大小(大约 26 MB)来作弊。这对于一些现实世界的场景来说可能是不可能的。不过,您始终可以使用“足够大的附加大小进行操作,然后截断文件。
更新 2(2019):
对于任何正在寻找现代(如 Java 11+)解决方案来解决问题的人,我会遵循 <一个href="https://stackoverflow.com/questions/7366266/best-way-to-write-string-to-file-using-java-nio/7367952?noredirect=1#comment95943399_7366266">@DodgyCodeException的建议 并使用
java.nio.file.Files.writeString
:I don't think you will be able to get a strict answer without benchmarking your software. NIO may speed up the application significantly under the right conditions, but it may also make things slower.
Here are some points:
rewind
andflip
? Seems like you are creating a new buffer for every string and just writing it to the channel. (If you go the NIO way, benchmark strategies that reuse the buffers instead of wrapping / discarding, I think they will do better).wrap
and allocateDirect may produce quite different buffers. Benchmark both to grasp the trade-offs. With direct allocation, be sure to reuse the same buffer in order to achieve the best performance.byte[]
orchar[]
buffer with a reasonable size as well). I've seen many, many, many people discovering that NIO is no silver bullet.If you fancy some bleeding edge... Back to IO Trails for some NIO2 :D.
And here is a interesting benchmark about file copying using different strategies. I know it is a different problem, but I think most of the facts and author conclusions also apply to your problem.
Cheers,
UPDATE 1:
Since @EJP tiped me that direct buffers wouldn't be efficient for this problem, I benchmark it myself and ended up with a nice NIO solution using nemory-mapped files. In my Macbook running OS X Lion this beats
BufferedOutputStream
by a solid margin. but keep in mind that this might be OS / Hardware / VM specific:I admit that I cheated a little by calculating the total size to append (around 26 MB) beforehand. This may not be possible for several real world scenarios. Still, you can always use a "big enough appending size for the operations and later truncate the file.
UPDATE 2 (2019):
To anyone looking for a modern (as in, Java 11+) solution to the problem, I would follow @DodgyCodeException's advice and use
java.nio.file.Files.writeString
:围绕 FileWriter 的 BufferedWriter 几乎肯定会比您能想到的任何 NIO 方案更快。您的代码当然不是最佳的,每次写入都有一个新的 ByteBuffer,然后在它即将超出范围时对其进行毫无意义的操作,但无论如何您的问题都是建立在误解之上的。 NIO 根本不会“将内存占用卸载到操作系统”,除非您使用 FileChannel.transferTo/From(),但在本例中您不能这样做。
注意,不要按照注释中的建议使用 PrintWriter,因为这会吞掉异常。 PW 实际上仅适用于您不关心的控制台和日志文件。
A BufferedWriter around a FileWriter will almost certainly be faster than any NIO scheme you can come up with. Your code certainly isn't optimal, with a new ByteBuffer per write, and then doing pointless operations on it when it is about to go out of scope, but in any case your question is founded on a misconception. NIO doesn't 'offload the memory footprint to the OS' at all, unless you're using FileChannel.transferTo/From(), which you can't in this instance.
NB don't use a PrintWriter as suggested in comments, as this swallows exceptions. PW is really only for consoles and log files where you don't care.
这是一个简短而简单的方法。它创建一个文件并写入与您的代码项目相关的数据:
Here is a short and easy way. It creates a file and writes the data relative to your code project:
这对我有用:
This works for me: