Java - 通过在读取文本文件时覆盖从文本文件中删除行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6477762/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 06:16:45  来源:igfitidea点击:

Java - delete line from text file by overwriting while reading it

javatext-filesprintwriter

提问by Shelley

I'm trying to delete a line of text from a text file without copying to a temporary file. I am trying to do this by using a Printwriter and a Scanner and having them traverse the file at the same time, the writer writing what the Scanner reads and overwriting each line with the same thing, until it gets to the line that I wish to delete. Then, I advance the Scanner but not the writer, and continue as before. Here is the code:

我试图从文本文件中删除一行文本而不复制到临时文件。我试图通过使用 Printwriter 和 Scanner 并让它们同时遍历文件来做到这一点,作者写下 Scanner 读取的内容并用相同的东西覆盖每一行,直到到达我希望的行删除。然后,我推进 Scanner 而不是 writer,并像以前一样继续。这是代码:

But first, the parameters: My file names are numbers, so this would read 1.txt or 2.txt, etc, and so f specifies the file name. I convert it to a String in the constructor for a file. Int n is the index of the line that I want to delete.

但首先,参数:我的文件名是数字,所以这将读取 1.txt 或 2.txt 等,因此 f 指定文件名。我在文件的构造函数中将其转换为字符串。Int n 是我要删除的行的索引。

public void deleteLine(int f, int n){
 try{
 Scanner reader = new Scanner(new File(f+".txt")); 
 PrintWriter writer = new PrintWriter(new FileWriter(new File(f+".txt")),false); 
 for(int w=0; w<n; w++)
   writer.write(reader.nextLine()); 
 reader.nextLine(); 
 while(reader.hasNextLine())
   writer.write(reader.nextLine());
 } catch(Exception e){
   System.err.println("Enjoy the stack trace!");
   e.printStackTrace();
 }
}

It gives me strange errors. It says "NoSuchElementException" and "no line found" in the stack trace. It points to different lines; it seems that any of the nextLine() calls can do this. Is it possible to delete a line this way? If so, what am I doing wrong? If not, why? (BTW, just in case you'd want this, the text file is about 500 lines. I don't know if that counts as large or even matters, though.)

它给了我奇怪的错误。它在堆栈跟踪中显示“NoSuchElementException”和“未找到行”。它指向不同的线;似乎任何 nextLine() 调用都可以做到这一点。是否可以通过这种方式删除一行?如果是这样,我做错了什么?如果不是,为什么?(顺便说一句,以防万一你想要这个,文本文件大约有 500 行。不过,我不知道这是否算大甚至重要。)

采纳答案by aioobe

As others have pointed out, you might be better off using a temporary file, if there's a slightest risk that your program crashes mid way:

正如其他人指出的那样,如果您的程序在中途崩溃的风险很小,那么您最好使用临时文件:

public static void removeNthLine(String f, int toRemove) throws IOException {

    File tmp = File.createTempFile("tmp", "");

    BufferedReader br = new BufferedReader(new FileReader(f));
    BufferedWriter bw = new BufferedWriter(new FileWriter(tmp));

    for (int i = 0; i < toRemove; i++)
        bw.write(String.format("%s%n", br.readLine()));

    br.readLine();

    String l;
    while (null != (l = br.readLine()))
        bw.write(String.format("%s%n", l));

    br.close();
    bw.close();

    File oldFile = new File(f);
    if (oldFile.delete())
        tmp.renameTo(oldFile);

}

(Beware of the sloppy treatment of encodings, new-line characters and exception handling.)

(注意编码、换行符和异常处理的草率处理。)



However, I don't like answering questions with "I won't tell you how, because you shouldn't do it anyway.". (In some other situation for instance, you may be working with a file that's larger than half your hard drive!) So here goes:

但是,我不喜欢用“我不会告诉你怎么做,因为无论如何你都不应该这样做。”来回答问题。(例如,在其他一些情况下,您可能正在处理一个大于一半硬盘驱动器的文件!)所以这里是:

You need to use a RandomAccessFileinstead. Using this class you can both read and write to the file using the same object:

您需要使用 aRandomAccessFile代替。使用此类,您可以使用相同的对象读取和写入文件:

public static void removeNthLine(String f, int toRemove) throws IOException {
    RandomAccessFile raf = new RandomAccessFile(f, "rw");

    // Leave the n first lines unchanged.
    for (int i = 0; i < toRemove; i++)
        raf.readLine();

    // Shift remaining lines upwards.
    long writePos = raf.getFilePointer();
    raf.readLine();
    long readPos = raf.getFilePointer();

    byte[] buf = new byte[1024];
    int n;
    while (-1 != (n = raf.read(buf))) {
        raf.seek(writePos);
        raf.write(buf, 0, n);
        readPos += n;
        writePos += n;
        raf.seek(readPos);
    }

    raf.setLength(writePos);
    raf.close();
}

回答by Hovercraft Full Of Eels

@shelley: you can't do what you are trying to do and what's more, you shouldn't. You should read the file and write to a temporary file for several reasons, for one, it's possible to do it this way (as opposed to what you're trying to do) and for another, if the process gets corrupted, you could bale out without loss of the original file. Now you could update a specific location of a file using a RandomAccessFile, but this is usually done (in my experience) when you are dealing with fixed sized records rather than typical text files.

@shelley:你不能做你想做的事,更重要的是,你不应该做。出于多种原因,您应该读取文件并写入临时文件,一方面,可以这样做(与您尝试执行的操作相反);另一方面,如果进程损坏,您可以打包不丢失原文件。现在您可以使用 RandomAccessFile 更新文件的特定位置,但这通常在您处理固定大小的记录而不是典型的文本文件时完成(根据我的经验)。

回答by Jarek Potiuk

You cannot do it this way. FileWriter can only append to a file, rather than write in the middle of it - You need RandomAccessFile if you want to write in the middle. What you do now - you override the file the first time you write to it (and it gets empty - that's why you get the exception). You can create FileWriter with append flag set to true - but this way you would append to a file rather than write in the middle of it.

你不能这样做。FileWriter 只能附加到一个文件,而不是在它的中间写入 - 如果要在中间写入,则需要 RandomAccessFile。您现在所做的 - 您在第一次写入文件时覆盖该文件(并且它变空 - 这就是您收到异常的原因)。您可以创建 FileWriter 并将附加标志设置为 true - 但这样您将附加到文件而不是在文件中间写入。

I'd really recommend to write to a new file and then rename it at the end.

我真的建议写入一个新文件,然后在最后重命名它。