Java: elimine la línea del archivo de texto sobrescribiéndola mientras la lee

Estoy intentando eliminar una línea de texto de un archivo de texto sin copiarlo en un archivo temporal. Estoy tratando de hacer esto usando un Printwriter y un Scanner y haciendo que atraviesen el archivo al mismo tiempo, el escritor escribe lo que el escáner lee y sobrescribe cada línea con la misma cosa, hasta que llega a la línea que deseo borrar. Luego, avanzo el escáner pero no el escritor, y continúo como antes. Aquí está el código:

Pero primero, los parámetros: Mis nombres de archivo son números, por lo que leerían 1.txt o 2.txt, etc., y así f especifica el nombre del archivo. Lo convierto en una cadena en el constructor para un archivo. Int n es el índice de la línea que quiero eliminar.

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

Me da errores extraños. Dice “NoSuchElementException” y “no se encontró línea” en el seguimiento de la stack. Apunta a diferentes líneas; parece que cualquiera de las llamadas nextLine () puede hacer esto. ¿Es posible eliminar una línea de esta manera? Si es así, ¿qué estoy haciendo mal? Si no, ¿por qué? (Por cierto, en caso de que quieras esto, el archivo de texto tiene alrededor de 500 líneas. No sé si eso cuenta como grande o incluso importa).

Como han señalado otros, es mejor que uses un archivo temporal, si existe el menor riesgo de que tu progtwig falle a mitad de camino:

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

(Tenga cuidado con el descuidado tratamiento de las codificaciones, los caracteres de nueva línea y el manejo de excepciones).


Sin embargo, no me gusta responder preguntas con " No te diré cómo, porque de todos modos no deberías hacerlo ". (En alguna otra situación, por ejemplo, ¡puede estar trabajando con un archivo que es más grande que la mitad de su disco duro!) Así que aquí va:

Necesita usar un RandomAccessFile lugar. Usando esta clase puedes leer y escribir en el archivo usando el mismo objeto:

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

No puedes hacerlo de esta manera. FileWriter solo se puede agregar a un archivo, en lugar de escribir en el medio del mismo. Necesita RandomAccessFile si desea escribir en el medio. Lo que haces ahora – anulas el archivo la primera vez que escribes (y se vacía) es por eso que obtienes la excepción. Puede crear FileWriter con el marcador append establecido en verdadero, pero de esta manera se agregaría a un archivo en lugar de escribir en el medio.

Realmente recomendaría escribir en un nuevo archivo y luego cambiarle el nombre al final.

@shelley: no puedes hacer lo que intentas hacer y, además, no deberías hacerlo. Debe leer el archivo y escribir en un archivo temporal por varias razones, por un lado, es posible hacerlo de esta manera (en lugar de por lo que está tratando de hacer) y por otro, si el proceso se corrompe, puede bloquearlo sin pérdida del archivo original. Ahora podría actualizar una ubicación específica de un archivo usando un RandomAccessFile, pero esto generalmente se hace (en mi experiencia) cuando se trata de registros de tamaño fijo en lugar de archivos de texto típicos.