26

我想创建一个简单的程序(在 Java 中)来编辑文本文件 - 特别是一个在文本文件中的随机位置插入任意文本片段的程序。这个功能是我目前正在编写的一个更大程序的一部分。

阅读有关 java.util.RandomAccessFile 的描述,似乎在文件中间执行的任何写入操作实际上都会覆盖现有内容。这是我想避免的副作用(如果可能的话)。

有没有一种简单的方法可以实现这一目标?

提前致谢。

4

8 回答 8

20

好的,这个问题已经很老了,但是 FileChannels 从 Java 1.4 开始就存在了,我不知道为什么在处理在文件中替换或插入内容的问题时没有提到它们。FileChannels很快,使用它们。

这是一个示例(忽略异常和其他一些东西):

public void insert(String filename, long offset, byte[] content) {
  RandomAccessFile r = new RandomAccessFile(new File(filename), "rw");
  RandomAccessFile rtemp = new RandomAccessFile(new File(filename + "~"), "rw");
  long fileSize = r.length();
  FileChannel sourceChannel = r.getChannel();
  FileChannel targetChannel = rtemp.getChannel();
  sourceChannel.transferTo(offset, (fileSize - offset), targetChannel);
  sourceChannel.truncate(offset);
  r.seek(offset);
  r.write(content);
  long newOffset = r.getFilePointer();
  targetChannel.position(0L);
  sourceChannel.transferFrom(targetChannel, newOffset, (fileSize - offset));
  sourceChannel.close();
  targetChannel.close();
}
于 2013-07-10T08:35:23.267 回答
11

嗯,不,我不相信有一种方法可以避免使用单个标准 Java IO API 调用覆盖现有内容。

如果文件不是太大,只需将整个文件读入 ArrayList(每行一个条目),然后重写条目或为新行插入新条目。

然后用新内容覆盖现有文件,或将现有文件移动到备份并写入新文件。

根据编辑需要的复杂程度,您的数据结构可能需要更改。

另一种方法是在写入已编辑文件时从现有文件中读取字符,并在读取流时对其进行编辑。

于 2008-11-14T12:57:33.327 回答
4

如果 Java 有内存映射文件的方法,那么您可以做的是将文件扩展至新长度,映射文件,将所有字节向下移动到末尾以创建一个孔并将新数据写入该孔。

这在 C 中有效。从未在 Java 中尝试过。

我刚刚想到的另一种方法,但使用随机文件访问。

  • 寻找到最后 - 1 MB
  • 读取 1 MB
  • 将其写入原始位置+间隙大小。
  • 对文件开头的每个前 1 MB 重复此操作。
  • 当您到达所需的间隙位置时停止。

使用更大的缓冲区大小以获得更快的性能。

于 2009-05-21T15:00:02.897 回答
4

您可以使用以下代码:

BufferedReader reader = null;
BufferedWriter writer = null;
ArrayList list = new ArrayList();

try {
    reader = new BufferedReader(new FileReader(fileName));
    String tmp;
    while ((tmp = reader.readLine()) != null)
        list.add(tmp);
    OUtil.closeReader(reader);

    list.add(0, "Start Text");
    list.add("End Text");

    writer = new BufferedWriter(new FileWriter(fileName));
    for (int i = 0; i < list.size(); i++)
        writer.write(list.get(i) + "\r\n");
} catch (Exception e) {
    e.printStackTrace();
} finally {
    OUtil.closeReader(reader);
    OUtil.closeWriter(writer);
}
于 2011-06-04T06:16:23.973 回答
2

我相信将文本插入现有文本文件的唯一方法是读取原始文件并将内容写入临时文件并插入新文本。然后擦除原始文件并将临时文件重命名为原始名称。

这个例子的重点是在现有文件中插入一行,但仍然可能对您有用。

于 2008-11-14T12:55:18.247 回答
2

我不知道是否有一种方便的方法可以直接做到这一点

  • 读取文件的开头并将其写入目标
  • 将您的新文本写入目标
  • 读取文件的其余部分并将其写入目标。

关于目标:您可以在内存中构造文件的新内容,然后如果处理的文件不是很大,则覆盖文件的旧内容。或者您可以将结果写入临时文件。

使用流可能最容易做到这一点, RandomAccessFile 似乎并不意味着插入中间(afaik)。如果需要,请查看教程。

于 2008-11-14T12:55:33.847 回答
1

如果是文本文件,,,,读取 StringBuffer 中的现有文件并将新内容附加到同一个 StringBuffer 中,现在您可以将 SrtingBuffer 写入文件。所以现在该文件包含现有文本和新文本。

于 2009-09-23T19:08:29.423 回答
0

由于@xor_eq 答案的编辑队列已满,因此在一个新答案中,他的版本更加有据可查且略有改进:

public static void insert(String filename, long offset, byte[] content) throws IOException {
    File temp = Files.createTempFile("insertTempFile", ".temp").toFile(); // Create a temporary file to save content to
    try (RandomAccessFile r = new RandomAccessFile(new File(filename), "rw"); // Open file for read & write
            RandomAccessFile rtemp = new RandomAccessFile(temp, "rw"); // Open temporary file for read & write
            FileChannel sourceChannel = r.getChannel(); // Channel of file
            FileChannel targetChannel = rtemp.getChannel()) { // Channel of temporary file
        long fileSize = r.length();
        sourceChannel.transferTo(offset, (fileSize - offset), targetChannel); // Copy content after insert index to
                                                                                // temporary file
        sourceChannel.truncate(offset); // Remove content past insert index from file
        r.seek(offset); // Goto back of file (now insert index)
        r.write(content); // Write new content
        long newOffset = r.getFilePointer(); // The current offset
        targetChannel.position(0L); // Goto start of temporary file
        sourceChannel.transferFrom(targetChannel, newOffset, (fileSize - offset)); // Copy all content of temporary
                                                                                    // to end of file
    }
    Files.delete(temp.toPath()); // Delete the temporary file as not needed anymore
} 
于 2021-10-05T21:49:03.570 回答