Java удалить строку из файла

Обновлено: 08.07.2024

Я пытаюсь удалить строку текста из текстового файла без копирования во временный файл. Я пытаюсь сделать это, используя Printwriter и Scanner, и пропуская их одновременно, автор пишет, что сканер читает и перезаписывает каждую строку с той же самой вещью, пока не дойдет до строки, которую я хочу Удалить. Затем я продвигаю сканер, но не писатель, и продолжаю по-прежнему. Вот код:

Но сначала параметры: Мои имена файлов - это числа, поэтому это будет читать 1.txt или 2.txt и т.д., и поэтому f указывает имя файла. Я конвертирую его в String в конструкторе для файла. Int n - это индекс строки, которую я хочу удалить.

Это дает мне странные ошибки. Он говорит "NoSuchElementException" и "строка не найдена" в трассировке стека. Он указывает на разные строки; кажется, что любой из вызовов nextLine() может это сделать. Можно ли удалить строку таким образом? Если да, то что я делаю неправильно? Если нет, то почему? (BTW, на всякий случай, если вы этого захотите, текстовый файл составляет около 500 строк. Я не знаю, имеет ли это значение как большое или даже важное значение.)

Как указывали другие, вам может быть полезно использовать временный файл, если есть хоть малейший риск, что ваша программа выйдет из строя в середине пути:

(Остерегайтесь неаккуратной обработки кодировок, символов новой строки и обработки исключений).

Однако мне не нравится отвечать на вопросы: "Я не скажу вам, как, потому что вы все равно не должны этого делать". (В некоторых других ситуациях, например, вы можете работать с файлом, который превышает половину вашего жесткого диска!) Итак, здесь идет:

Вместо этого вам нужно использовать RandomAccessFile . Используя этот класс, вы можете читать и записывать в файл с использованием одного и того же объекта:

Вы не можете этого сделать. FileWriter может добавлять только файл, а не писать в середине. Вам нужно RandomAccessFile, если вы хотите писать в середине. Что вы сейчас делаете - вы переопределяете файл при первом его написании (и он становится пустым - вот почему вы получаете исключение). Вы можете создать FileWriter с флагом присоединения, установленным в true, но таким образом вы бы добавили в файл, а не записывали его в середине.

Я бы рекомендовал написать новый файл, а затем переименовать его в конце.

Я пытаюсь удалить строку текста из текстового файла без копирования во временный файл. Я пытаюсь сделать это, используя Printwriter и сканер и заставляя их одновременно пересекать файл, писатель пишет то, что читает сканер, и перезаписывает каждую строку одним и тем же, пока не доберется до строки, которую я хочу удалить. Затем я продвигаю сканер, но не писатель, и продолжаю, как и раньше. Вот код:

но сначала параметры: мой файл имена-это числа, так что это будет 1.txt или 2.txt и т. д., и поэтому f указывает имя файла. Я преобразую его в строку в конструкторе для файла. Int n-индекс строки, которую я хочу удалить.

Это дает мне странные ошибки. В нем говорится "NoSuchElementException" и "строка не найдена" в трассировке стека. Он указывает на разные линии; кажется, что любой из вызовов nextLine() может это сделать. Можно ли удалить строку таким образом? Если да, то что я делаю не так? Если нет, почему? (Кстати, на всякий случай, если вам это нужно, текстовый файл составляет около 500 строк. Я не знаю, считается ли это большим или даже имеет значение.)

как указывали другие, вам может быть лучше использовать временный файл, если есть малейший риск того, что ваша программа вылетит в середине пути:

(остерегайтесь небрежной обработки кодировок, символов новой строки и обработки исключений.)

тем не менее, я не люблю отвечать на вопросы с "Я не скажу вам, как, потому что вы все равно не должны этого делать.". (В какой-то другой ситуации, например, вы можете работать с файлом, который больше половины вашего жесткого диска!) Так вот:

вы не можете сделать это таким образом. FileWriter может только добавлять к файлу, а не писать в середине его - вам нужен RandomAccessFile, если вы хотите писать в середине. То, что вы делаете сейчас - вы переопределяете файл при первой записи в него (и он становится пустым - вот почему вы получаете исключение). Вы можете создать FileWriter с флагом append, установленным в true, но таким образом вы добавите файл, а не напишете в его середине.

Я бы очень рекомендовал написать новому файл, а затем переименовать его в конце.

@shelley: вы не можете делать то, что вы пытаетесь сделать, и более того, вы не должны. Вы должны прочитать файл и записать его во временный файл по нескольким причинам: Во-первых, это можно сделать таким образом (в отличие от того, что вы пытаетесь сделать), а во-вторых, если процесс будет поврежден, вы можете сбросить без потери исходного файла. Теперь вы можете обновить определенное местоположение файла с помощью RandomAccessFile, но это обычно делается (по моему опыту), когда вы имеете дело с фиксированный размер записи, а не типичные текстовые файлы.

Узнайте различные способы удаления содержимого файла без удаления файла.

1. Введение

В этом учебнике Мы увидим, как мы используем Java для удаления содержимого файла без удаления самого файла. Поскольку есть много простых способов сделать это, давайте рассмотрим каждый по одному.

2. Использование PrintWriter

Ява РаспечататьПисач класс расширяет Писатель класс. Он печатает отформатированную представление объектов в потоке текстового вывода.

Мы выполнить простой тест. Давайте создадим РаспечататьПисач например, указывая на существующий файл, удаляя существующее содержимое файла, просто закрывая его, а затем убедитесь, что длина файла пуста:

Кроме того, обратите внимание, что если нам не нужна РаспечататьПисач объект для дальнейшей обработки, это лучший вариант. Однако, если нам нужна РаспечататьПисач объект для дальнейших операций файла, мы можем сделать это по-другому:

3. Использование Файлрайтер

Ява ФайлРайтер является стандартным классом Java IO API, который предоставляет методы записи данных, ориентированных на характер, в файл.

Давайте теперь посмотрим, как мы можем сделать ту же операцию с помощью ФайлРайтер:

Точно так же, если нам нужна ФайлРайтер объект для дальнейшей обработки, мы можем назначить его переменной и обновить с пустой строкой.

4. Использование FileOutputStream

FileOutputStream Java — это выходной поток, используемый для записи данных byte в файл.

Теперь давайте удалим содержимое файла с помощью FileOutputStream:

5. Использование Apache Commons IO FileUtils

Чтобы увидеть, как это работает, давайте добавим ИО- 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. Заключение

Подводя итог, мы видели несколько способов удаления содержимого файла без удаления самого файла.

Java имеет два файловых API.

Оригинал java.io . Файл API, доступный с Java 1.0 (1996).

Более новый java.nio.файл. Путь API, доступный с Java 1.7 (2011).

В чем разница между API-интерфейсами File и Path?

Старый файловый API используется во множестве старых проектов, фреймворков и библиотек. Несмотря на свой возраст, он не устарел (и, вероятно, никогда не устареет), и вы все еще можете использовать его с любой из последних версий Java.

Тем не менее, java.nio.файл. Путь делает все java.io . Файл может, но в целом лучше и больше. Несколько примеров:

Функции файла: Новые классы поддерживают символические ссылки, соответствующие атрибуты файлов и поддержку метаданных (например, атрибуты PosixFileAttributes), списки управления доступом и многое другое.

Развязка: Включение поддержки файловых систем в памяти, о которой мы расскажем позже.

Какой файловый API мне следует использовать?

По причинам, упомянутым выше, если вы начинаете новый проект Java, настоятельно рекомендуется использовать Пути API поверх Файла API. (Хотя файл читается намного приятнее, чем путь , не так ли?)

Следовательно, в этой статье мы сосредоточимся исключительно на Путях API.

Путь.of: Как ссылаться на файлы

Для работы с файлами на Java вам сначала понадобится ссылка на файл (большой сюрприз!). Как мы только что упомянули выше, начиная с Java 7, вы будете использовать API путей для ссылки на файлы, поэтому все начинается с построения Пути объектов.

Давайте посмотрим какой-нибудь код.

Давайте разберемся с этим:

Начиная с Java 11, вы должны использовать статический метод Path.of для построения путей (мы рассмотрим эквивалент Java 7-10 через секунду).

Не имеет значения, используете ли вы прямые косые черты, например, в Windows, так как API Path достаточно умен, чтобы построить правильный путь, независимо от операционной системы и любых проблем с прямой и обратной косой чертой.

Таким образом, обе строки выше вернут следующий результат при запуске основного метода.

При построении путей у вас есть больше возможностей выбора: вам не нужно указывать полный путь в виде одной длинной строки:

Вместо этого вы можете передать последовательность строк в метод Path.of или создать родительский каталог и использовать его для получения дочернего файла ( .resolve(дочерний) ).

Опять же, результат будет таким же, как и раньше.

И последнее, но не менее важное: вы также можете передавать URI в Путь.из вызова.

Итак, у вас есть множество вариантов построения объектов вашего пути.

Однако два важных момента:

Path.of назывался Paths.get , который вам нужно будет использовать, если вы застряли на более старых версиях Java или создаете библиотеку, которая нуждается в некоторой обратной совместимости. Начиная с Java 11, Paths.get внутренне перенаправляется на Path.of .

Как только у вас есть объект path, вы можете, наконец, сделать что-то с ним. Давайте посмотрим, что и как в следующем разделе.

Файлы: Общие операции

При работе с файлами или путями вы, скорее всего, будете использовать файл java.nio. Файлы класс. Он содержит множество распространенных и полезных статических методов, которые работают с файлами и каталогами.

Используйте этот раздел в качестве краткой шпаргалки, заголовки не требуют пояснений.

Как проверить, существует ли файл

Как получить дату последнего изменения файла

Как сравнивать файлы (Java 12+)

Как узнать владельца файла

Как создавать временные файлы

Примечание : Временные файлы, вопреки распространенному мнению, не удаляются сами. Вы должны убедиться, что явно удалили их при создании в модульных тестах или при запуске в рабочей среде.

Как создавать файлы и каталоги

Вы видели, как создавать временные файлы, и то же самое происходит с обычными файлами и каталогами. Вы просто вызовете разные методы:

Как получить разрешения Posix для файла

Запустив это в Linux или macOS, вы получите такой результат:

Запись и чтение Файлов

Как записывать строки в файлы

Мы еще не говорили о сути обработки файлов: записи в файлы и чтении из них.

Давайте посмотрим, как вы можете это сделать:

И последнее, но не менее важное: если вы хотите напрямую работать с записывающими устройствами или потоками вывода, обязательно вызовите соответствующие методы Files , а не создавайте записи или потоки вручную.

Как читать строки из файлов

Чтение файлов очень похоже на запись:

Как всегда, вы можете вернуться к использованию считывателей или потоков ввода напрямую. Для этого используйте соответствующие Файлы методы.

Дружеское напоминание: Кодировки файлов

Я упоминал об этом пару раз в предыдущих разделах:

Вы абсолютно должны использовать явную кодировку при создании, записи или чтении файлов, хотя очень помогает то, что новые методы Java 11 по умолчанию используют UTF-8, а не кодировку, зависящую от платформы.

Перемещение, Удаление и Перечисление Файлов

Есть несколько вещей, на которые вам нужно обратить внимание при перемещении или удалении файлов. Давайте посмотрим какой-нибудь код:

Как перемещать файлы

Существует метод Files.move , но он не перемещает файл в указанный каталог (что вы могли бы ожидать).

test.jpg → c:\temp не работает.

test.jpg → c:\temp\test.jpg работает.

Параметры Перемещения Файлов

При перемещении файлов вы также можете указать, как вы хотите, чтобы перемещение происходило, в зависимости от возможностей базовой файловой системы.

По умолчанию, если целевой файл уже существует, будет создано исключение FileAlreadyExistsException .

Если вы укажете параметр standardcopy. Опция REPLACE_EXISTING , целевой файл будет перезаписан.

Если вы укажете параметр standardcopy. Опция ATOMIC_MOVE позволяет переместить файл в каталог и гарантировать, что любой процесс, наблюдающий за каталогом, получит доступ к полному файлу, а не только к частичному файлу.

Как удалить файлы

Там есть Файлы.метод удаления , который позволяет удалять файлы и каталоги, но каталоги только в том случае, если они пусты.

К сожалению, нет флага для очистки непустого каталога, и вы просто получите исключение DirectoryNotEmptyException .

Как удалить непустые каталоги

Для решения этой проблемы существуют некоторые сторонние вспомогательные библиотеки, но если вы хотите использовать обычную версию Java для удаления непустого дерева каталогов, это то, что вы захотите сделать:

Файлы.walk будет проходить по глубине дерева файлов, начиная с указанного вами каталога. Компаратор в обратном порядке позаботится о том, чтобы вы удалили всех дочерних элементов, прежде чем удалять фактический каталог.

К сожалению, вам также потребуется перехватить исключение IOException при использовании Файлов.удалите внутри для каждого потребителя. Целая куча кода для удаления непустого каталога, не так ли?

Что подводит нас к теме перечисления файлов:

Как перечислить файлы в одном каталоге

Существуют различные способы перечисления всех файлов в данном каталоге. Если вы хотите перечислять файлы только на тех же уровнях, что и каталог (не рекурсивно глубже), вы можете использовать эти два метода:

Обратите внимание, что newDirectoryStream (в отличие от Файлов.список ) не возвращает java.util.stream. Поток . Вместо этого он возвращает Каталог Stream , который является классом, представленным в Java 1.7, до выпуска API потоков в Java 8.

Это, однако, позволяет вам указать шаблон glob (например, *.txt), который выполняет работу для простых списков, и, возможно, его немного легче читать, чем возиться с реальными потоками и соответствующими фильтрами методами.

Как рекурсивно перечислять файлы

Если вы хотите рекурсивно перечислить все файлы в файловом дереве, вам нужно будет использовать метод, который мы использовали для удаления каталогов: Файлы.прогулка .

Абсолютные, Относительные и Канонические файлы

Давайте быстро поговорим о понятиях абсолютных, относительных и канонических путей. Это лучше всего продемонстрировать на некоторых примерах кода:

Относительные Пути

Абсолютные пути

Нормализованные Пути

Как избавиться от точек? Вам нужно будет позвонить нормализовать .

И последнее, но не менее важное: вы также можете пойти другим путем. Вместо того, чтобы делать относительные пути абсолютными, вы можете сделать абсолютные пути относительными.

Просмотр Файлов и каталогов

Некоторым проектам необходимо просматривать каталоги для вновь созданных (думайте: загруженных) файлов и что-то с ними делать. У вас есть два популярных варианта, когда дело доходит до просмотра изменений в каталоге на Java.

Служба наблюдения Java

С Java 7, Java его Служба просмотра . Это несколько низкоуровневый способ отслеживания изменений в указанном каталоге.

Служба наблюдения будет получать уведомления о событиях собственного файла (

Вот некоторый код, который вы не должны слепо копировать и вставлять ,

Есть пара вещей, на которые следует обратить внимание (без каламбура) при использовании WatchService:

Вы можете предположить, что получаете одно событие, например, при обновлении файла, но это может легко привести к двум событиям: одному для обновленного содержимого и одному для обновления последней измененной метки времени, происходящим в течение короткого периода времени.

Сложные IDE, такие как IntelliJ, или даже более мелкие текстовые редакторы, такие как Notepad++, не просто сохраняют файл и его содержимое за один раз. Они копируют содержимое в файлы tmp, удаляют их, затем сохраняют содержимое в ваш фактический файл и т. Д. Опять же, может быть несколько обновлений, происходящих с одним и тем же или даже несколькими файлами, в то время как вы, как конечный пользователь, в идеале хотели бы иметь только одно обновленное событие.

Следовательно, вам нужно будет применить некоторые обходные пути . Неприемлемый ответ с 40+ голосами ( Thread.sleep ) в прошлом работал для меня несколько надежно).

И последнее, но не менее важное: вы, возможно, захотите взглянуть на эту превосходную статью , в которой рассказывается о службе наблюдения Java, контейнерах и проблемах с монтированием привязки.

Apache Commons-ИО

Есть еще одна библиотека, которая позволяет просматривать каталоги на предмет входящих изменений: Commons IO . Он имеет более простой API с точки зрения использования, но отличается от службы Watch двумя аспектами:

Это работает только с java.io . Файлы , а не java.nio.файл. Пути .

Он использует опрос, т. Е. вызывает метод listFiles() класса File и сравнивает вывод с выводом listFiles() предыдущей итерации, чтобы увидеть, что изменилось.

Опять же, полная реализация выходит за рамки этой статьи, но вы, возможно, захотите взглянуть на это Gist для примера рабочего кода или используйте javadoc на FileAlterationMonitor или Сервер обработки файлов в качестве отправной точки.

Вот как примерно выглядит код, который вы не должны слепо копировать и вставлять :

Файловые Системы В Памяти

Некоторые разработчики предполагают, что работа с файлами всегда означает, что вам действительно придется записывать их на свой диск.

Во время тестирования это приводит к созданию большого количества временных файлов и каталогов, а затем к необходимости их повторного удаления.

Но с Java Path -API есть гораздо лучший способ: Файловые системы в памяти.

Есть две файловые системы Java в памяти, на которые стоит обратить внимание.

Файловая система Памяти

Давайте разберемся с этим.

Вызывая новый Linux() или новую Windows() или новую Mac Os() вы можете управлять семантикой созданной файловой системы.

Вы пишете в файл под названием somefile.txt и чтение содержимого файла через пару строк.

Это обычный java.nio.файл. Путь-API , с одной огромной разницей. Вам нужно получить свой путь из файловой системы , не через Путь.из или Пути.получить .

Вы поймете, почему это так, посмотрев на Джима.

У Джима

Давайте разберемся с этим.

С помощью параметра Configuration.unix/windows/macOS вы можете управлять семантикой созданной файловой системы.

Вы пишете в файл под названием somefile.txt и чтение содержимого файла через пару строк.

Это обычный java.nio.файл. Путь-API , с одной огромной разницей. Вам нужно получить свой путь из файловой системы , не через Путь.из или Пути.получить .

Давайте теперь посмотрим, почему это так.

Как заставить ваше приложение работать с файловыми системами в памяти: якоря

Когда вы смотрите на реализацию Path.of или Пути.получить , вы увидите это:

Таким образом, хотя этот метод (и другие) очень удобны, их использование будет означать, что вы хотите получить доступ к своему по умолчанию Файловая система, на которой работает ваша JVM (файловая система Windows, файловая система Unix и т. Д.), не ваша в памяти Файловая система.

Следовательно, если вы хотите убедиться, что ваш код работает с файловыми системами в памяти, вы должны убедиться, что никогда не вызываете эти методы помощников. Вместо этого вы всегда должны использовать Файловую систему или Путь в качестве привязки, как вы делаете в приведенных выше примерах.

В зависимости от вашего проекта (подумайте: наследие), это довольно сложная задача.

Плавник

К настоящему времени у вас должно быть довольно хорошее представление о том, как работать с файлами на Java.

Как выполнять все основные операции с файлами, начиная с чтения, записи, перечисления, перемещения и удаления.

Как работают относительные, абсолютные и канонические пути.

Как просматривать каталоги и файлы.

Как вы можете использовать файловые системы в памяти для тестирования.

Обратная связь, исправления и случайный ввод всегда приветствуются! Просто оставьте комментарий ниже.

Читайте также: