Find a line in a file and remove it

后端 未结 16 1838
谎友^
谎友^ 2020-11-22 13:06

I\'m looking for a small code snippet that will find a line in file and remove that line (not content but line) but could not find. So for example I have in a file following

相关标签:
16条回答
  • 2020-11-22 13:40

    This solution uses a RandomAccessFile to only cache the portion of the file subsequent to the string to remove. It scans until it finds the String you want to remove. Then it copies all of the data after the found string, then writes it over the found string, and everything after. Last, it truncates the file size to remove the excess data.

    public static long scanForString(String text, File file) throws IOException {
        if (text.isEmpty())
            return file.exists() ? 0 : -1;
        // First of all, get a byte array off of this string:
        byte[] bytes = text.getBytes(/* StandardCharsets.your_charset */);
    
        // Next, search the file for the byte array.
        try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {
    
            List<Integer> matches = new LinkedList<>();
    
            for (long pos = 0; pos < file.length(); pos++) {
                byte bite = dis.readByte();
    
                for (int i = 0; i < matches.size(); i++) {
                    Integer m = matches.get(i);
                    if (bytes[m] != bite)
                        matches.remove(i--);
                    else if (++m == bytes.length)
                        return pos - m + 1;
                    else
                        matches.set(i, m);
                }
    
                if (bytes[0] == bite)
                    matches.add(1);
            }
        }
        return -1;
    }
    
    public static void remove(String text, File file) throws IOException {
        try (RandomAccessFile rafile = new RandomAccessFile(file, "rw");) {
            long scanForString = scanForString(text, file);
            if (scanForString == -1) {
                System.out.println("String not found.");
                return;
            }
            long remainderStartPos = scanForString + text.getBytes().length;
            rafile.seek(remainderStartPos);
            int remainderSize = (int) (rafile.length() - rafile.getFilePointer());
            byte[] bytes = new byte[remainderSize];
            rafile.read(bytes);
            rafile.seek(scanForString);
    
            rafile.write(bytes);
            rafile.setLength(rafile.length() - (text.length()));
        }
    }
    

    Usage:

    File Contents: ABCDEFGHIJKLMNOPQRSTUVWXYZ

    Method Call: remove("ABC", new File("Drive:/Path/File.extension"));

    Resulting Contents: DEFGHIJKLMNOPQRSTUVWXYZ

    This solution could easily be modified to remove with a certain, specifiable cacheSize, if memory is a concern. This would just involve iterating over the rest of the file to continually replace portions of size, cacheSize. Regardless, this solution is generally much better than caching an entire file in memory, or copying it to a temporary directory, etc.

    0 讨论(0)
  • 2020-11-22 13:44

    I refactored the solution that Narek had to create (according to me) a slightly more efficient and easy to understand code. I used embedded Automatic Resource Management, a recent feature in Java and used a Scanner class which according to me is more easier to understand and use.

    Here is the code with edited Comments:

    public class RemoveLineInFile {
    
        private static File file;
    
        public static void main(String[] args) {
            //create a new File
            file = new File("hello.txt");
            //takes in String that you want to get rid off
            removeLineFromFile("Hello");
        }
    
    
        public static void removeLineFromFile(String lineToRemove) {
    
    
            //if file does not exist, a file is created
    
                if (!file.exists()) {
                    try {
                        file.createNewFile();
                    } catch (IOException e) {
                        System.out.println("File "+file.getName()+" not created successfully");
                    }
                }
    
                // Construct the new temporary file that will later be renamed to the original
                // filename.
                File tempFile = new File(file.getAbsolutePath() + ".tmp");
    
               //Two Embedded Automatic Resource Managers used
                // to effectivey handle IO Responses
              try(Scanner scanner = new Scanner(file)) {
                  try (PrintWriter pw = new PrintWriter(new FileWriter(tempFile))) {
    
                      //a declaration of a String Line Which Will Be assigned Later
                      String line;
    
                      // Read from the original file and write to the new
                      // unless content matches data to be removed.
                      while (scanner.hasNextLine()) {
                          line = scanner.nextLine();
                          if (!line.trim().equals(lineToRemove)) {
    
                              pw.println(line);
                              pw.flush();
                          }
                      }
                      // Delete the original file
                      if (!file.delete()) {
                          System.out.println("Could not delete file");
                          return;
                      }
    
                      // Rename the new file to the filename the original file had.
                      if (!tempFile.renameTo(file))
                          System.out.println("Could not rename file");
                  }
              }
            catch (IOException e)
            {
                System.out.println("IO Exception Occurred");
            }
    
        }
    
    
    
    }
    
    0 讨论(0)
  • 2020-11-22 13:47

    Here you go. This solution uses a DataInputStream to scan for the position of the string you want replaced and uses a FileChannel to replace the text at that exact position. It only replaces the first occurrence of the string that it finds. This solution doesn't store a copy of the entire file somewhere, (either the RAM or a temp file), it just edits the portion of the file that it finds.

    public static long scanForString(String text, File file) throws IOException {
        if (text.isEmpty())
            return file.exists() ? 0 : -1;
        // First of all, get a byte array off of this string:
        byte[] bytes = text.getBytes(/* StandardCharsets.your_charset */);
    
        // Next, search the file for the byte array.
        try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {
    
            List<Integer> matches = new LinkedList<>();
    
            for (long pos = 0; pos < file.length(); pos++) {
                byte bite = dis.readByte();
    
                for (int i = 0; i < matches.size(); i++) {
                    Integer m = matches.get(i);
                    if (bytes[m] != bite)
                        matches.remove(i--);
                    else if (++m == bytes.length)
                        return pos - m + 1;
                    else
                        matches.set(i, m);
                }
    
                if (bytes[0] == bite)
                    matches.add(1);
            }
        }
        return -1;
    }
    
    public static void replaceText(String text, String replacement, File file) throws IOException {
        // Open a FileChannel with writing ability. You don't really need the read
        // ability for this specific case, but there it is in case you need it for
        // something else.
        try (FileChannel channel = FileChannel.open(file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ)) {
            long scanForString = scanForString(text, file);
            if (scanForString == -1) {
                System.out.println("String not found.");
                return;
            }
            channel.position(scanForString);
            channel.write(ByteBuffer.wrap(replacement.getBytes(/* StandardCharsets.your_charset */)));
        }
    }
    

    Example

    Input: ABCDEFGHIJKLMNOPQRSTUVWXYZ

    Method Call:

    replaceText("QRS", "000", new File("path/to/file");
    

    Resulting File: ABCDEFGHIJKLMNOP000TUVWXYZ

    0 讨论(0)
  • 2020-11-22 13:49
    public static void deleteLine(String line, String filePath) {
    
        File file = new File(filePath);
    
        File file2 = new File(file.getParent() + "\\temp" + file.getName());
        PrintWriter pw = null;
        Scanner read = null;
    
        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel src = null;
        FileChannel dest = null;
    
        try {
    
    
            pw = new PrintWriter(file2);
            read = new Scanner(file);
    
            while (read.hasNextLine()) {
    
                String currline = read.nextLine();
    
                if (line.equalsIgnoreCase(currline)) {
                    continue;
                } else {
                    pw.println(currline);
                }
            }
    
            pw.flush();
    
            fis = new FileInputStream(file2);
            src = fis.getChannel();
            fos = new FileOutputStream(file);
            dest = fos.getChannel();
    
            dest.transferFrom(src, 0, src.size());
    
    
        } catch (IOException e) {
            e.printStackTrace();
        } finally {     
            pw.close();
            read.close();
    
            try {
                fis.close();
                fos.close();
                src.close();
                dest.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            if (file2.delete()) {
                System.out.println("File is deleted");
            } else {
                System.out.println("Error occured! File: " + file2.getName() + " is not deleted!");
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 13:50

    Old question, but an easy way is to:

    • Iterate through file, adding each line to an new array list
    • iterate through the array, find matching String, then call the remove method.
    • iterate through array again, printing each line to the file, boolean for append should be false, which basically replaces the file
    0 讨论(0)
  • 2020-11-22 13:52

    So, whenever I hear someone mention that they want to filter out text, I immediately think to go to Streams (mainly because there is a method called filter which filters exactly as you need it to). Another answer mentions using Streams with the Apache commons-io library, but I thought it would be worthwhile to show how this can be done in standard Java 8. Here is the simplest form:

    public void removeLine(String lineContent) throws IOException
    {
        File file = new File("myFile.txt");
        List<String> out = Files.lines(file.toPath())
                            .filter(line -> !line.contains(lineContent))
                            .collect(Collectors.toList());
        Files.write(file.toPath(), out, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
    }
    

    I think there isn't too much to explain there, basically Files.lines gets a Stream<String> of the lines of the file, filter takes out the lines we don't want, then collect puts all of the lines of the new file into a List. We then write the list over top of the existing file with Files.write, using the additional option TRUNCATE so the old contents of the file are replaced.

    Of course, this approach has the downside of loading every line into memory as they all get stored into a List before being written back out. If we wanted to simply modify without storing, we would need to use some form of OutputStream to write each new line to a file as it passes through the stream, like this:

    public void removeLine(String lineContent) throws IOException
    {
        File file = new File("myFile.txt");
        File temp = new File("_temp_");
        PrintWriter out = new PrintWriter(new FileWriter(temp));
        Files.lines(file.toPath())
            .filter(line -> !line.contains(lineContent))
            .forEach(out::println);
        out.flush();
        out.close();
        temp.renameTo(file);
    }
    

    Not much has been changed in this example. Basically, instead of using collect to gather the file contents into memory, we use forEach so that each line that makes it through the filter gets sent to the PrintWriter to be written out to the file immediately and not stored. We have to save it to a temporary file, because we can't overwrite the existing file at the same time as we are still reading from it, so then at the end, we rename the temp file to replace the existing file.

    0 讨论(0)
提交回复
热议问题