简体   繁体   English

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

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

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.我试图通过使用 Printwriter 和 Scanner 并让它们同时遍历文件来做到这一点,作者写下 Scanner 读取的内容并用相同的东西覆盖每一行,直到到达我希望的行删除。 Then, I advance the Scanner but not the writer, and continue as before.然后,我推进 Scanner 而不是 writer,并像以前一样继续。 Here is the code:这是代码:

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.但首先,参数:我的文件名是数字,所以这将读取 1.txt 或 2.txt 等,因此 f 指定文件名。 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. 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.它在堆栈跟踪中显示“NoSuchElementException”和“未找到行”。 It points to different lines;它指向不同的线; it seems that any of the nextLine() calls can do this.似乎任何 nextLine() 调用都可以做到这一点。 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.) (顺便说一句,以防万一你想要这个,文本文件大约有 500 行。不过,我不知道这是否算大甚至重要。)

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 RandomAccessFile instead.您需要改用RandomAccessFile Using this class you can both read and write to the file using the same object:使用此类,您可以使用相同的对象读取和写入文件:

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();
}

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. FileWriter 只能附加到一个文件,而不是在它的中间写入 - 如果要在中间写入,则需要 RandomAccessFile。 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 并将附加标志设置为 true - 但这样您将附加到文件而不是在文件中间写入。

I'd really recommend to write to a new file and then rename it at the end.我真的建议写入一个新文件,然后在最后重命名它。

@shelley: you can't do what you are trying to do and what's more, you shouldn't. @shelley:你不能做你想做的事,更重要的是,你不应该做。 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.现在您可以使用 RandomAccessFile 更新文件的特定位置,但这通常在您处理固定大小的记录而不是典型的文本文件时完成(根据我的经验)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM