【问题标题】:Removing a line from a txt file java [closed]从txt文件java中删除一行[关闭]
【发布时间】:2014-10-05 11:44:19
【问题描述】:

我有一个大文件,我只需要删除其中的几行 有没有办法在不打开新文件并复制整个文本的情况下做到这一点?

编辑: 主要问题是当它在一个以上的线程中运行时,带有大 txt 文件的程序会失败

【问题讨论】:

标签: java string


【解决方案1】:

有没有办法在不打开新文件并复制整个文本的情况下做到这一点?

不,没有。当然,如果您想安全地这样做,则没有。

RandomAccessFile 也不会真正帮助您。它允许您用相同数量的字节替换文件中的字节序列,但这并不等于删除一行。

您可以像这样使用 RAF:

给定初始状态 L1L2L3...LNL2L3...LN 替换为 L3...LN

或者您可以使用 RAF 根据@halfbit 的回答一次“滑动”一行。

但是:

  • 在最坏的情况下,您会复制整个文件内容,而一般情况下涉及读取和写入 O(N) 行的字节。

  • 执行此操作的简单方法需要在内存中保存O(N) 行。

  • “滑动”方法需要O(N) I/O 操作(即系统调用)。

  • 最重要的是:通过就地文件更新删除行是有风险的。如果应用程序在进程中间中断(例如电源故障),那么您最终会得到一个损坏的文件。

FWIW:这不是 Java 的限制本身。相反,它是现代操作系统表示/模型文件方式的限制。

【讨论】:

    【解决方案2】:

    查看Random Access Files,以便将文件指针定位在所需位置并移动文本。

    【讨论】:

      【解决方案3】:

      这是一些使用RandomAccessFile 删除行而不打开新文件 的独立示例代码,这似乎对我有用。 (不过需要就地复制。)

      public static void main(String[] args) {
          try {
              // prepare test file
              String path = "/tmp/test.txt";
              writeTestLines(path, 999999);
      
              // mode "rws": read + write synchronous
              RandomAccessFile raf = new RandomAccessFile(path, "rws");
      
              int bufSize = 1 << 20; // 1 MiB
              Scanner s = new Scanner(new BufferedInputStream(new FileInputStream(raf.getFD()), bufSize));
              PrintWriter pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(raf.getFD()), bufSize));
              long writeOffset = 0;
              for (int nr = 1;; nr++) {
                  if (!s.hasNextLine())
                      break;
                  String line = s.nextLine();
                  if (nr != 2 && !line.contains("00")) {
                      // switch to writing: save read offset, seek write offset
                      long readOffset = raf.getFilePointer();
                      raf.seek(writeOffset);
                      pw.println(line);
                      // switch to reading: save write offset, seek read offset
                      writeOffset = raf.getFilePointer();
                      raf.seek(readOffset);
                  }
              }
      
              // write buffered output and truncate file
              raf.seek(writeOffset);
              pw.flush();
              raf.setLength(raf.getFilePointer());
      
              pw.close();
              s.close();
              raf.close();
          } catch (Exception ex) {
              ex.printStackTrace(System.err);
          }
      }
      
      public static void writeTestLines(String path, int n) throws IOException {
          PrintWriter pw = new PrintWriter(path);
          for (int i = 1; i <= n; i++) pw.println("line " + i);
          pw.close();
      }
      

      请注意,此代码假定 Scanner 读取的行尾与 PrintWriter 产生的行尾相同(例如,在 Windows 上不仅仅是一个 LineFeed)。

      请注意,上面的代码可以优化为不重写任何未更改的文件头 - 例如只需先跟踪写入偏移量,然后切换到“普通”PrintWriter。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-28
        • 2020-06-15
        • 2018-03-21
        • 1970-01-01
        相关资源
        最近更新 更多