Java удалить строку из файла
Узнайте различные способы удаления содержимого файла без удаления файла.
1. Введение
В этом учебнике Мы увидим, как мы используем Java для удаления содержимого файла без удаления самого файла. Поскольку есть много простых способов сделать это, давайте рассмотрим каждый по одному.
2. Использование PrintWriter
Ява РаспечататьПисач класс расширяет Писатель класс. Он печатает отформатированную представление объектов в потоке текстового вывода.
Мы выполнить простой тест. Давайте создадим РаспечататьПисач например, указывая на существующий файл, удаляя существующее содержимое файла, просто закрывая его, а затем убедитесь, что длина файла пуста:
Кроме того, обратите внимание, что если нам не нужна РаспечататьПисач объект для дальнейшей обработки, это лучший вариант. Однако, если нам нужна РаспечататьПисач объект для дальнейших операций файла, мы можем сделать это по-другому:
3. Использование Файлрайтер
Ява ФайлРайтер является стандартным классом Java IO API, который предоставляет методы записи данных, ориентированных на характер, в файл.
Давайте теперь посмотрим, как мы можем сделать ту же операцию с помощью ФайлРайтер:
Точно так же, если нам нужна ФайлРайтер объект для дальнейшей обработки, мы можем назначить его переменной и обновить с пустой строкой.
4. Использование FileOutputStream
FileOutputStream Java — это выходной поток, используемый для записи данных byte в файл.
Теперь давайте удалим содержимое файла с помощью FileOutputStream:
5. Использование Apache Commons IO FileUtils
ИО- Apache Commons это библиотека, которая содержит классы утилиты, чтобы помочь с общими проблемами IO. Мы можем удалить содержимое файла с помощью одного из его классов утилиты – ФилеУтилс.
Чтобы увидеть, как это работает, давайте добавим ИО- Apache Commons зависимость от нашей пом.xml :
После этого возьмем быстрый пример, демонстрирующий удаление содержимого файла:
6. Использование файлов Java NIO
Мы также можем удалить содержимое файла с помощью java.nio.file.Files :
7. Использование Java NIO FileChannel
Java NIO FileChannel — это реализация NIO для подключения файла. Он также дополняет стандартный пакет Java IO.
8. Использование Гуавы
Guava — это библиотека с открытым исходным кодом на основе Java, которая предоставляет полезные методы для ведения операций в i/O. Давайте посмотрим, как использовать API Guava для удаления содержимого файла.
Во-первых, мы должны добавить Гуава зависимости в нашей пом.xml :
После этого рассмотрим быстрый пример удаления содержимого файла с помощью Guava:
9. Заключение
Подводя итог, мы видели несколько способов удаления содержимого файла без удаления самого файла.
Есть у меня программа типа словаря, в которой есть кнопка удалить. Задача такая, мне нужно найти строку в текстовом файле, и удалить её. Находить, я нахожу, но вот удалять строку я не научился. Пробовал такой вот метод
Но получается пока удалять все содержимое из файла, а надо только заранее найденную строку.
3 ответа 3
В приведённом Вами коде присутствует сразу несколько грубейших ошибок:
- Вы записываете данные сразу в тот же файл из которого их только что прочитали;
- Вы записываете в файл пустую строку;
- После записи первой строки Вы сразу же закрываете файл.
В итоге получается описанный Вами результат.
Чтобы этого не было:
Создаём две файловые переменные.
File sourceFile = new File("Dictio.txt"); File outputFile = new File("Dictio2.txt");
Записываем в новый файл все данные, кроме удаляемой строки.
BufferedReader reader = new BufferedReader(new FileReader(sourceFile)); BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile)); while ((line = reader.readLine()) != null) < if (!line.equals(outputLine)) < writer.write(line); writer.newLine(); >>
Затем удаляем исходный файл, а получившийся файл переименовываем.
reader.close(); writer.close(); sourceFile.delete(); outputFile.renameTo(sourceFile);
P.S.
В данной ситуации, ИМХО, более рациональным решением было бы прочитать файл в строковый массив, а затем удалить в нём нужную строку и перезаписать исходный файл с новыми данными.
Ошибок нет. Алгоритм рабочий. Судя по всему, что-то сделали не так. Просто, нужно хотя бы немного разобраться, а не копировать в свой проект "в лоб". Без обид. Выкладывайте код, как пытались применить решение из ответа, и stack trace исключения. Комментарии из категории "у меня не получается и всё" здесь не прокатывают.
Как вставлять код в комментарии я не знаю (F.A.Q. показанный внизу я не понял). Но делал примерно так, сначала задал outputLine так, как задал его в своем первоначальном варианте. Затем объявил переменную таким образом- String line; . Потом объявил два файла как указанно у вас в примере. Основную часть с записью в файл кроме удаляемой строки я окружил try/catch. И в этом же блоке перед самым catch задал удаление исходного и переименовывание нового файла. И исключение выглядит так java.io.IOException: Stream closed
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:
myFile.txt:
Need to have a function like this: public void removeLine(String lineContent) , and if I pass removeLine("bbb") , I get file like this:
myFile.txt:
@AnkeshkumarJaisansaria Using a FileChannel can help avoid caching the entire file in question, either in RAM or at a temporary location, but would require re-writing all the bytes subsequent to the line that needs to be removed, I believe. See this for more details on using a FileChannel .
@AnkeshkumarJaisansaria I just made this which actually removes a matched string in a file. This solution uses a RandomAccessFile instead of a FileChannel , but still doesn't have the overhead or setbacks of copying an entire file or caching an entire file into memory.
17 Answers 17
This solution may not be optimal or pretty, but it works. It reads in an input file line by line, writing each line out to a temporary output file. Whenever it encounters a line that matches what you are looking for, it skips writing that one out. It then renames the output file. I have omitted error handling, closing of readers/writers, etc. from the example. I also assume there is no leading or trailing whitespace in the line you are looking for. Change the code around trim() as needed so you can find a match.
You should add a writer.close() before the rename. Otherwise the writer might not have flushed the last line or two to disk.
the renameTo in the last line will fail, since the inputFile is existing. Before it, we should execute inputFile.delete();
This I have found on the internet.
This code won't close the files if an exception is thrown, you need a finally statement in there too.
You want to do something like the following:
- Open the old file for reading
- Open a new (temporary) file for writing
- Iterate over the lines in the old file (probably using a BufferedReader)
- For each line, check if it matches what you are supposed to remove
- If it matches, do nothing
- If it doesn't match, write it to the temporary file
(I won't write the actual code, since this looks like homework, but feel free to post other questions on specific bits that you have trouble with)
Any solution which doesn't involve creating a temp file? Some soln which would write in the same file.
@AniketSinha Sure. Just make sure you have fully read the file into an array/buffer/string and operate on that (since you can overwrite the original file once you have read it all in)
Using apache commons-io and Java 8 you can use
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 Stream s 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:
I think there isn't too much to explain there, basically Files.lines gets a Stream 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:
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.
При разработке проекта постала необходимость удалять файлы, созданные приложением во время своего выполнения. Но требовалось, чтобы файлы удалялись не по завершению сеанса, работы ПК, а по требованию пользователя.
И кажется, в этом проблемы нет. В стандартной библиотеке Java есть метод delete() в классе java.io.File для удаления файла.
Метод delete() в классе java.io.File вызывает под капотом нативную функцию для удаления файла в зависимости от ОС. А современные ОС при удалении файла сразу не удаляют файл, а только удаляют имя файла. Содержимое файла остается, и память занимаемая под уже удаленный файл может быть в будущем переиспользована. Но все таки некоторое время кажется уже удаленный файл является доступный.И если посмотреть на просторы интернета, то имеется немало программ для восстановления удаленных файлов, например Recuva.
Но хочется чтобы файлы были удалены без возможности востановления их в будущем. Начав искать в интернете, оказывается удаление файла без восстановление (затирание) очень не тривиальная задача. И при реализации такой задачи требуется учитывать особенности работы с файлами в определенной ОС. И при этом нужно для этого использовать либо вручную написанное нативное API или какую-то нативную библиотеку.
Поскольку приложения разрабатовалось в Ubuntu, то эта ОС предоставляет немало готовых решений в виде утилит командной строки. Например, утилита secure-delete, которая позволяет удалять файлы без востановления используя разные подходы.
Приложения должно проверять установлена ли утилита и выводить ошибку если не находит. Но в случае использованием приложения для другой ОС, то нужно использовать подобную утилиту.Что очень не удобно и хочется уйти от этих проблем. Если посмотреть исходный код утилиты secure-delete, то она позволяет работать под разные операционные системы. Написана на С99 и использует разную препроцессорную магию и платформо-зависимый API. Отлаживать такой нативный код в случае ошибки очень сложно и еще та задача.
Если разобраться как работает утилита secure-delete, то можно выделить следующие этапы.
- сначала проверяется существует ли файл и корректность прав.
- в зависимости от указаного алгоритма перезаписывает содержимое файла.
- сокращает размер файла к нуль байтам.
- переименовует файл рандомной последовательностю символов.
- удаляет файл.
- Simple алгоритм — перезаписывает 1 проходом 0x00 байтами.
- DOE алгоритм — перезаписывает 3 проходами random, random, «DoE».
- RCMP алгоритм — перезаписывает 3 проходами 0x00 ,0xFF, «RCMP».
- OPENBSD алгоритм — перезаписывает 3 проходами 0xFF, 0x00, 0xFF байтами.
- DOD алгоритм — перезаписывает 7 проходами.
- Gutmann алгоритм — перезаписывает 35 проходами.
Для того чтобы проверить существует ли файл и имеет ли он корректные права можно использовать std::filesystem, которая была добавлена в C++17.
Для предыдущих версий стандарта можно использовать boost::filesystem.
Для перезаписывания содержимого файла в зависимости от выбраного алгортма можно оставить реализацию, как в secure-delete.
Заполняется буфер определеного размера, определеным набором данных, в зависимости от алгоритма и записывает это буфер в файл, пока не достигнет конца.
Потом сократим размер файла к нуль байтам, используя для этого функцию std::filesystem::resize_file().
Следующим этапом переименовуем файл рандомной последовательностю символов, используя для этого std::random() и std::filesystem::file::replace_filename().
И на завершающем этапе нужно просто удалить файл, используя для этого std::filesystem::remove().
Ну и для использования на Java нужно объявить нативные методы.
Нативная реализация, использует только стандартную библиотеку С++, что позволяет легко портировать на другие платформы. И главное нету никакой препроцессорной макросной магии, которую не так легко отлаживать в случае ошибок.Стандарт С++17 уже поддерживают все популярные компиляторы: MVSC, Clang, GCC.
Полный исходной код можно посмотреть на github: code.Я пытаюсь удалить строку текста из текстового файла без копирования во временный файл. Я пытаюсь сделать это, используя Printwriter и сканер и заставляя их одновременно пересекать файл, писатель пишет то, что читает сканер, и перезаписывает каждую строку одним и тем же, пока не доберется до строки, которую я хочу удалить. Затем я продвигаю сканер, но не писатель, и продолжаю, как и раньше. Вот код:
но сначала параметры: мой файл имена-это числа, так что это будет 1.txt или 2.txt и т. д., и поэтому f указывает имя файла. Я преобразую его в строку в конструкторе для файла. Int n-индекс строки, которую я хочу удалить.
Это дает мне странные ошибки. В нем говорится "NoSuchElementException" и "строка не найдена" в трассировке стека. Он указывает на разные линии; кажется, что любой из вызовов nextLine() может это сделать. Можно ли удалить строку таким образом? Если да, то что я делаю не так? Если нет, почему? (Кстати, на всякий случай, если вам это нужно, текстовый файл составляет около 500 строк. Я не знаю, считается ли это большим или даже имеет значение.)
как указывали другие, вам может быть лучше использовать временный файл, если есть малейший риск того, что ваша программа вылетит в середине пути:
(остерегайтесь небрежной обработки кодировок, символов новой строки и обработки исключений.)
тем не менее, я не люблю отвечать на вопросы с "Я не скажу вам, как, потому что вы все равно не должны этого делать.". (В какой-то другой ситуации, например, вы можете работать с файлом, который больше половины вашего жесткого диска!) Так вот:
вы не можете сделать это таким образом. FileWriter может только добавлять к файлу, а не писать в середине его - вам нужен RandomAccessFile, если вы хотите писать в середине. То, что вы делаете сейчас - вы переопределяете файл при первой записи в него (и он становится пустым - вот почему вы получаете исключение). Вы можете создать FileWriter с флагом append, установленным в true, но таким образом вы добавите файл, а не напишете в его середине.
Я бы очень рекомендовал написать новому файл, а затем переименовать его в конце.
@shelley: вы не можете делать то, что вы пытаетесь сделать, и более того, вы не должны. Вы должны прочитать файл и записать его во временный файл по нескольким причинам: Во-первых, это можно сделать таким образом (в отличие от того, что вы пытаетесь сделать), а во-вторых, если процесс будет поврежден, вы можете сбросить без потери исходного файла. Теперь вы можете обновить определенное местоположение файла с помощью RandomAccessFile, но это обычно делается (по моему опыту), когда вы имеете дело с фиксированный размер записи, а не типичные текстовые файлы.
Читайте также: