Java-удалить строку из текстового файла путем перезаписи при чтении
Я пытаюсь удалить строку текста из текстового файла без копирования во временный файл. Я пытаюсь сделать это, используя Printwriter и сканер и заставляя их одновременно пересекать файл, писатель пишет то, что читает сканер, и перезаписывает каждую строку одним и тем же, пока не доберется до строки, которую я хочу удалить. Затем я продвигаю сканер, но не писатель, и продолжаю, как и раньше. Вот код:
но сначала параметры: мой файл имена-это числа, так что это будет 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();
}
}
Это дает мне странные ошибки. В нем говорится "NoSuchElementException" и "строка не найдена" в трассировке стека. Он указывает на разные линии; кажется, что любой из вызовов nextLine() может это сделать. Можно ли удалить строку таким образом? Если да, то что я делаю не так? Если нет, почему? (Кстати, на всякий случай, если вам это нужно, текстовый файл составляет около 500 строк. Я не знаю, считается ли это большим или даже имеет значение.)
3 ответов
как указывали другие, вам может быть лучше использовать временный файл, если есть малейший риск того, что ваша программа вылетит в середине пути:
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);
}
(остерегайтесь небрежной обработки кодировок, символов новой строки и обработки исключений.)
тем не менее, я не люблю отвечать на вопросы с "Я не скажу вам, как, потому что вы все равно не должны этого делать.". (В какой-то другой ситуации, например, вы можете работать с файлом, который больше половины вашего жесткого диска!) Так вот:
вам нужно использовать RandomAccessFile
. Используя этот класс, вы можете читать и записывать в файл, используя один и тот же объект:
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();
}
вы не можете сделать это таким образом. FileWriter может только добавлять к файлу, а не писать в середине его - вам нужен RandomAccessFile, если вы хотите писать в середине. То, что вы делаете сейчас - вы переопределяете файл при первой записи в него (и он становится пустым - вот почему вы получаете исключение). Вы можете создать FileWriter с флагом append, установленным в true, но таким образом вы добавите файл, а не напишете в его середине.
Я бы очень рекомендовал написать новому файл, а затем переименовать его в конце.
@shelley: вы не можете делать то, что вы пытаетесь сделать, и более того, вы не должны. Вы должны прочитать файл и записать его во временный файл по нескольким причинам: Во-первых, это можно сделать таким образом (в отличие от того, что вы пытаетесь сделать), а во-вторых, если процесс будет поврежден, вы можете сбросить без потери исходного файла. Теперь вы можете обновить определенное местоположение файла с помощью RandomAccessFile, но это обычно делается (по моему опыту), когда вы имеете дело с фиксированный размер записи, а не типичные текстовые файлы.