Java8/NIO中有没有任何机制可以在不将大文件加载到内存的情况下替换大文件的行?
我基本上是在寻找一个解决方案,它允许我流式处理这些行,并将它们替换到同一个文件中,即la Files.lines Java8/NIO中有没有任何机制可以在不将大文件加载到内存的情况下替换大文件的行 基本上没有 对文件的任何更改,如果涉及更改OFFET a和B之间的字节数,则只能通过重写文件或创建新文件来完成。在这两种情况下,B之后的所有内容都必须加载/读取到内存中 这不是特定于Java的限制。这是现代操作系统表示文件的方式和低级ie的结果。它们提供给应用程序的系统调用API 在使用完全相同长度的行或行序列替换一行或行序列的特定情况下,可以使用RandomAccessFile或通过将文件映射到内存来进行替换。请注意,后一种方法不会导致将整个文件读入内存Java8/NIO中有没有任何机制可以在不将大文件加载到内存的情况下替换大文件的行?,java,stream,nio,Java,Stream,Nio,我基本上是在寻找一个解决方案,它允许我流式处理这些行,并将它们替换到同一个文件中,即la Files.lines Java8/NIO中有没有任何机制可以在不将大文件加载到内存的情况下替换大文件的行 基本上没有 对文件的任何更改,如果涉及更改OFFET a和B之间的字节数,则只能通过重写文件或创建新文件来完成。在这两种情况下,B之后的所有内容都必须加载/读取到内存中 这不是特定于Java的限制。这是现代操作系统表示文件的方式和低级ie的结果。它们提供给应用程序的系统调用API 在使用完全相同长度的
也可以在更新文件时替换或删除行,同时更改文件长度。。。。有关示例,请参见@Sergio Montoro的答案。但是,使用就地更新时,如果应用程序中断,则存在文件损坏的风险。这确实涉及到在插入/删除点之后读取和重写文件中的所有字节。这需要将它们加载到内存中。Java1中有一种机制:随机访问文件;但是,任何这样的就地机制都需要知道直线的起始偏移量,并且新线的长度与旧线的长度相同 否则,您必须将文件复制到该行,替换输出中的新行,然后继续复制 您当然不必将整个文件加载到内存中。是的 FileChannel允许对文件的任何位置进行随机读/写。因此,如果有足够长的预读缓冲区,即使新行比前一行长,也可以替换行 下面的示例是一个玩具实现,它做出了两个假设:第一,输入文件是ISO-8859-1 Unix LF编码的;第二,每一新行永远不会比下一行长一行预读缓冲区 除非您确实无法创建临时文件,否则您应该将此方法与更自然的stream in->stream out进行比较,因为我不知道旋转驱动器为文件中不断向前和向后移动的算法提供什么性能
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import static java.nio.file.StandardOpenOption.*;
import java.io.IOException;
public class ReplaceInFile {
public static void main(String args[]) throws IOException {
Path file = Paths.get(args[0]);
ByteBuffer writeBuffer;
long readPos = 0l;
long writePos;
String line_m;
String line_n;
String line_t;
FileChannel channel = FileChannel.open(file, READ, WRITE);
channel.position(0);
writePos = readPos;
line_m = readLine(channel);
do {
readPos += line_m.length() + 1;
channel.position(readPos);
line_n = readLine(channel);
line_t = transformLine(line_m)+"\n";
writeBuffer = ByteBuffer.allocate(line_t.length()+1);
writeBuffer.put(line_t.getBytes("ISO8859_1"));
System.out.print("replaced line "+line_m+" with "+line_t);
channel.position(writePos);
writeBuffer.rewind();
while (writeBuffer.hasRemaining()) {
channel.write(writeBuffer);
}
writePos += line_t.length();
line_m = line_n;
assert writePos > readPos;
} while (line_m.length() > 0);
channel.close();
System.out.println("Done!");
}
public static String transformLine(String input) throws IOException {
return input.replace("<", "<").replace(">", ">");
}
public static String readLine(FileChannel channel) throws IOException {
ByteBuffer readBuffer = ByteBuffer.allocate(1);
StringBuffer line = new StringBuffer();
do {
int read = channel.read(readBuffer);
if (read<1) break;
readBuffer.rewind();
char c = (char) readBuffer.get();
readBuffer.rewind();
if (c=='\n') break;
line.append(c);
} while (true);
return line.toString();
}
}
要执行您建议的操作,您的文件需要以允许这种更改的方式进行格式化。i、 e.数据结构。平面文本文件需要从您更改它的位置重新写入,除非您不更改纵向文本文件。是否可以选择在进程中使用shell命令?就像在Linux上一样,您可以发出某种sed命令。。。我知道这会带来像操作系统依赖性这样的缺点,但如果这是你唯一的机会…@Fildor sed不会在原地运行。@EJP-啊,对了…这种机制仍然存在。尽管NIO提供了内存映射,但对于该用例,内存映射可能具有更高的性能。如果修改后的字符串的长度与源字符串的长度相同,则可以这样做,否则,如果目标字符串短于源字符串,则可以在文件中保留原始行的跟踪。如果您添加一个channel.truncatewritePos-1;最后,它工作得很好。