Удалить строку из текстового файла delphi
Я работаю с текстовым файлом в Delphi, и я не хочу использовать метод загрузки / сохранения со списком строк. Я намерен поддерживать открытый файловый поток, в котором я читаю и записываю туда свои данные, сохраняя огромные объемы данных на жестком диске, а не в памяти. У меня есть простая концепция записи новых строк в текстовый файл и их чтения, но когда дело доходит до их изменения и удаления, я не могу найти никаких хороших ресурсов.
Каждая строка в этом файле содержит имя и знак равенства, а остальное - данные. Например, SOMEUNIQUENAME=SomeStringValue . Я собираюсь держать файл открытым в течение некоторого времени внутри потока. Этот поток выполняет входящие запросы на получение, установку или удаление определенных полей данных. Я использую WriteLn и ReadLn в цикле, оценивая EOF . Ниже приведен пример того, как я читаю данные:
У меня есть процедуры, готовые для записи и удаления этих полей, но я не знаю, что мне нужно сделать, чтобы на самом деле выполнить действие с файлом.
В конце концов, мне нужно избегать загрузки всего файла в память, чтобы иметь возможность сделать это.
3 ответа
Мне это интересный вопрос, поэтому я сделал небольшое консольное приложение.
Я использовал 3 метода:
- TStringList
- Streamreader / StreamWriter
- Текстовый файл
Все методы рассчитаны по времени и повторяются 100 раз с текстовым файлом размером 10 КБ и текстовым файлом размером 1 МБ. Вот программа:
Как видите, победителем здесь является TStringList. Поскольку вы не можете использовать TStringList, TextFile в конце концов не плохой выбор .
P.S. : этот код опускает часть, где вы должны удалить входной файл и переименовать выходной файл в исходное имя файла
Без загрузки всего файла в контейнер, например TStringList , ваш единственный вариант:
- Откройте файл для ввода
- Открыть отдельную копию для вывода
- Начать цикл
- Прочитать содержимое построчно из входного файла
- Записывайте содержимое построчно в выходной файл, пока не дойдете до строки, которую хотите изменить / удалить.
- Разорвать петлю
- Прочитать строку ввода из входного файла
- Запишите измененную строку (или пропустите запись строки, которую вы хотите удалить) в выходной файл
- Начать новый цикл
- Прочтите оставшуюся часть введенного содержимого построчно
- Запишите остальную часть этого ввода в выходной файл, строка за строкой.
- Разорвать петлю
- Закройте файлы
Итак, чтобы ответить на ваши конкретные вопросы:
Запишите новый вывод во второй (выходной) файл.
Просто пропустите WriteLn , который выводит указанную строку во второй (выходной) файл.
Ваше искусственное ограничение «Я не хочу использовать TStringList» просто усложняет вам задачу, когда вы можете просто:
- Загрузите исходный файл в TStringList с помощью LoadFromFile
- Найдите строку, которую хотите изменить, по индексу, итерации или IndexOf()
- Измените строку, изменив ее напрямую или удалив из TStringList
- Запишите все содержимое в исходный файл с помощью TStringList.SaveToFile
Я обнаружил, что единственные причины, по которым я не использовать TStringList для выполнения таких операций, заключаются в том, что размер файла превышает емкость TStringList (никогда не было) или когда работа с файлом, который является текстовым, но на самом деле не ориентирован на «строку» (например, файлы EDI, которые обычно представляют собой одну очень длинную одиночную строку текста, или файлы XML, которые могут не содержать переводы строк и, следовательно, также являются одной очень длинной одиночной строкой строка текста). Однако даже в случае EDI или XML довольно часто их загружают в TStringList , выполняют преобразование в строковый формат (вставляя разрывы строк или что-то еще) и выполняют поиск из списка строк.
По сути, вы не можете делать то, что хотите, если обрабатываете файлы как простые текстовые файлы. Такие файлы могут быть прочитаны (только с начала) или записаны (либо с начала, создавая таким образом новый файл), либо с конца (добавление к существующему файлу). Это не файлы с произвольным доступом.
С другой стороны, вы можете захотеть определить файл со строковым типом: каждая запись в файле будет строкой, и вы можете получить доступ к этому файлу случайным образом. Тогда проблема заключается в том, чтобы узнать, к какой записи и к какой строке обращаться.
Третья возможность - использовать файлы INI, которые более структурированы и кажутся более подходящими для ваших целей. Помимо заголовка раздела, они представляют собой серию строк, ключ = значение, и к ним можно получить доступ на основе ключа.
Всем привет !
Есть текстовый файл где около 100 тыс. строк. Нужно с заданой строки удалить все строки которые шли до нее. Допустим задать 2500 строку и удалить все 2499 строк до нее.
Как можно это осуществить ? Если есть где-то ман по работе с текстом и строками, пожалуйста поделитесь ссылочкой.
Буду примного благодарен всем кто поможет
Добавлено через 13 часов 32 минуты
Весь google перерыл. Везде одна и та же статья по работе со строками, а то чего нужно нет
Удаление строк из текстового файла
Задача: есть текстовой файл(а) который содержит много текста допустим объявления. есть еще один.
Удаление строк из текстового файла
Добрый день! Помогите пожалуйста решить задачу: Есть текстовый файл с большим количеством.
Удаление из текстового документа строк, не удовлетворяющих условию
Доброго времени суток. Существует текстовый документ, содержащий большое количество строк (около.
удаление пробелы из текстового файла
как можно удалить пробелы между словами которых ты написал в Мемо, и надо вывести правильный ответ.
1. Открыть файл на чтение.
2. Организовать перебор строк и поиск искомой строки
3. Если строка найдена, по произвести запись всех остальных строк в temp.txt
4. Удалить файл.
5. Переименовать temp.txt в ваш файл.
1. Открыть файл на чтение.
2. Организовать перебор строк и поиск искомой строки
3. Если строка найдена, по произвести запись всех остальных строк в temp.txt
4. Удалить файл.
5. Переименовать temp.txt в ваш файл.
А собственно совет, то дельный, делаешь перебор по строкам символов так по 1025.
доходишь до нужного тебе номера и дальше с пункта 3.
О чем вы говорите - при современных процессорах .У меня удалить все строки до миллионной - заняло 4 секунды.
И опять же потоки - это только эмуляция одновременной работы.Если в одном потоке +++++
а во втором ---------, то результат будет такой +-+-+-+-
G4W2, предложил нормальный вариант
А я хочу предложить свой
удаляться будут не те строки(i увеличивается а число строк в st уменьшается - итог - удаляются не те строки)
во-вторых две описки
for i:=StrToInt(Edit1.TExt); to n do st.delete(k); // Удаляем строки
st.SAveTofile(mytext.txt); // сохраняем измененный текст в файл
точка с запятой в середине первой строки и название файла во второй строке без ' '
Добавлено через 9 минут
Алучше вообще так
удаляться будут не те строки(i увеличивается а число строк в st уменьшается - итог - удаляются не те строки)
Добрый день, ну вот никак мне не нравится эта строка, поскольку если файл допустим будет 8 gb то атупеет нето комп нето пользователь ждать пока файл загрузиться (опустим факт что таким способом не более 2 гб) но всеже если вы знаете способ более дельный то скажите как удалить из текстового файла с позиции n до позиции k без использования api.
А зачем вообще циклы и прочая лабуда?
Ну сначала про тысячи строк. Подгружайте частями, найдёте нужное место, и тогда - вперёд.
А вперёд, это элементарное копирование потока. Да, это требует памяти. Но количество памяти и скорость всегда были конкурентами. Хотите скорость - гоните памяти побольше, хотите меньше памяти, готовьтесь к длительному процессу со свопингами.
Правда после того, как я связался с KOL я уже основательно подзабыл возможности файловых потоков в основном делфи. Но в KOL я делаю это так.
Открываю файловый поток. Частями загружаю в мемо, или куда там ещё, текст. Получаю позицию обрезки.
Потом открываю другой файловый поток. И копирую в него из первого потока с нужной позиции нужное количество данных. Операция эта хорошо оптимизирована, использует механизм буферизации, и проблем никаких.
Только сомневаюсь чтобы текст был хотя-бы на гиг.
Кстати, в KOL при записи из ричедит (не помню как с мемо) так же нужно определять позицию начала записи и количество записываемых данных. Так что, стоит положится на благоразумность и оптимальность кода записи и попросту, найдя место вырезки, произвести запись нового файла с этими параметрами.
сем привет !
Есть текстовый файл где около 100 тыс. строк. Нужно с заданой строки удалить все строки которые шли до нее. Допустим задать 2500 строку и удалить все 2499 строк до нее.
Как можно это осуществить ? Если есть где-то ман по работе с текстом и строками, пожалуйста поделитесь ссылочкой.
Буду примного благодарен всем кто поможет
есть возможность удалять непосредственно из файла с позиции setfilepointer до n длинной байт и без всей этой лобуды которую предложили ниже.
Я пробовал использовать эту кодировку для выполнения задачи:
Но я не уверен, что поместить в «индексное» пространство.
Есть ли способ получить идентификатор пользователя в качестве ввода, а затем удалить строку текста из текстового файла, в котором есть этот идентификатор пользователя? Любая помощь будет оценена.
Итак, в вашем примере, вот так
Это может быть медленным для больших файлов, так как IndexOfName проходит цикл через каждую строку в TStringList и проверяет каждую строку по очереди, пока не найдет совпадение.
Отказ от ответственности: Протестировано / работает с Delphi 2007, Delphi 7 может отличаться.
Для тех, кто любит однострочники. Это тоже работает:
Объяснение
- Он загружает содержимое файла в строку.
- Строка отправляется в TRegEx.Replace
- Регулярное выражение ищет имя пользователя, за которым следует знак решетки, затем любой символ, а затем CRLF. Он заменяет его пустой строкой.
- Полученная строка затем записывается в исходный файл.
Это просто для удовольствия, потому что я видел длинный код, в котором я думал, что это возможно с помощью одной строки кода.
До сих пор все предлагали использовать цикл For..Then , но могу ли я предложить Повторить .. Пока .
Традиционный For..Loop - хороший вариант, но может быть неэффективным, если у вас длинный список имен пользователей (обычно они уникальны). После обнаружения и удаления цикл For продолжается до конца списка. Это нормально, если у вас небольшой список, но если у вас 500 000 имен пользователей и одно из них, которое вам нужно, находится на позиции 10 000, нет причин для продолжения после этого момента.
Поэтому попробуйте это.
После вызова функция возвращает True или False, указывая, что имя пользователя было удалено или нет.
Есть единственный способ «удалить строку из текстового файла» - это создать новый файл с измененным содержимым, ПЕРЕЗАПИСАТЬ его.
Так что лучше просто сделайте это явно.
И не забывайте о защите от ошибок. Ваш текущий код может просто уничтожить файл и утечку памяти, если произойдет какая-либо ошибка .
Примечание 1. Посмотрите, должна ли проверка имени пользователя быть чувствительной к регистру или без учета регистра:
Примечание 2: в Delphi 7 SizeOf( s[1] ) всегда равно единице, потому что string является псевдонимом для AnsiString . Но в более новой версии Delphi это не так. Это может показаться утомительным и излишним, но в будущем может избавить от головной боли. Еще лучше было бы иметь временную переменную типа AnsiString , например a := AnsiString( s + ^m^J ); fs.WriteBuffer(a[1],Length(a));
Ради интереса вот компактное решение, которое мне нравится за удобочитаемость.
Очевидно, это не самое эффективное решение. Это может работать быстрее, если не добавлять отдельные элементы в массив или кэшировать строку поиска.
Я не понимаю, почему так много людей делают это так сложно. Это довольно просто:
Как говорит Arioch'The, если вы сохраните файл с тем же именем, вы рискуете потерять свои данные при сбое сохранения, поэтому вы можете сделать что-то вроде
Это сохраняет резервную копию исходного файла как FileName + '.old' .
Пояснения
Работаем в обратном направлении
Зачем работать в обратном направлении? Потому что, если у вас есть следующие предметы
И вы удаляете элемент в ^ , тогда следующие элементы сдвинутся вниз:
Если вы выполните итерацию вперед, вы теперь укажете на
И E никогда не проверяется. Если вы вернетесь назад, вы укажете на:
Обратите внимание, что E , F и G уже были проверены, так что теперь вы действительно изучите следующий элемент, C , и не пропустите ни одного. Кроме того, если вы пойдете вверх с помощью 0 to Count - 1 и удалите, Count станет на единицу меньше, и в конце вы попытаетесь получить доступ за границу списка. Этого не может произойти, если вы работаете в обратном направлении, используя Count - 1 downto 0 .
Я попытался использовать эту кодировку для выполнения задачи:
но я не уверен, что поместить в пространство "индекс".
есть ли способ получить идентификатор пользователя в качестве входных данных, а затем удалить строку текста из текстового файла с этим идентификатором пользователя? Любая помощь будет оценена.
так в вашем примере, вот так
это может быть медленно для больших файлов, как циклы IndexOfName, хотя каждая строка в TStringList и проверяет каждую строку по очереди, пока не найдет совпадение.
отказ от ответственности: протестировано / работает с Delphi 2007, Delphi 7 может быть различным.
я не понимаю, почему так много людей делают это так сложно. Это довольно просто:
как Ариох там говорит, Если вы сохраните с тем же именем файла, вы рискуете потерять свои данные при этом сэкономить не удастся, так что вы можете сделать что-то вроде
это сохраняет резервную копию исходного файла как FileName + '.old' .
объяснениями
работает назад
зачем работать в обратном направлении? Потому что если у вас есть следующие пункты
и вы удаляете элемент в ^ , то следующие элементы будут смещаться вниз:
если вы повторите вперед, теперь вы укажете на
и E никогда не исследовал. Если вы пойдете назад, то вы укажете на:
отметим, что E , F и G были осмотрены уже, так что теперь вы действительно рассмотрите следующий пункт, C и вы не пропустите ни. Также, если вы идите вверх, используя 0 to Count - 1 и удалить Count станет одним меньше, и в конце вы попытаетесь получить доступ за границу списка. Это не может произойти, если вы работаете в обратном направлении, используя Count - 1 downto 0 .
есть единственный способ "удалить строку из текстового файла" - то есть создать новый файл с измененным содержимым, чтобы переписать его.
поэтому вам лучше просто сделать это явно.
и не забывайте о защите от ошибок. Ваш текущий код может просто уничтожить файл и утечку памяти, если произойдет какая-либо ошибка.
Примечание 1: см., если проверка имени пользователя должна быть чувствительной к регистру или случай-игнорирование:
примечание 2: в Delphi 7 SizeOf( s[1] ) всегда равно единице, потому что string - это псевдоним для AnsiString . Но в более новой версии Delphi это не так. Это может показаться утомительным и избыточным, но это может сэкономить много головной боли в будущем. Еще лучше бы быть иметь временное AnsiString введите переменную типа a := AnsiString( s + ^m^J ); fs.WriteBuffer(a[1],Length(a));
до сих пор все предлагали использовать для For..Тогда цикл, но могу ли я предложить повторить..В то время как.
традиционный For..Петля является хорошим вариантом, но может быть неэффективным, если у вас есть длинный список имен пользователей (они обычно уникальны). После обнаружения и удаления на цикл продолжается до конца списка. Это нормально, если у вас есть небольшой список, но если у вас есть 500 000 пользователей и вы хотите в позицию 10 000 нет смысла продолжать дальше.
поэтому попробуйте это.
после вызова функция возвращает True или False, указывая, что имя пользователя было удалено или нет.
просто для удовольствия, вот компактное решение, которое мне нравится за его читаемость.
очевидно, это не самое эффективное решение. Это может выполняться быстрее, не добавляя отдельные элементы в массив или кэшируя строку поиска.
для тех, кто любит шутки. Это тоже работает:
объяснение
- он загружает содержимое файла в строку.
- строка отправляется в TRegEx.Заменить
- регулярное выражение ищет имя пользователя, за которым следует знак хэша, затем любой символ, а затем CRLF. Он заменяет его пустой строкой.
- полученная строка затем записывается в оригинал файл
Это просто для удовольствия, потому что я увидел длинный код, где я думал, что это будет возможно с одной строки кода.
Я пробовал использовать эту кодировку для выполнения задачи:
Но я не уверен, что поместить в «индексное» пространство.
Есть ли способ получить идентификатор пользователя в качестве ввода, а затем удалить строку текста из текстового файла, в котором есть этот идентификатор пользователя? Любая помощь будет оценена.
Я думаю, вам нужно просмотреть каждую строку sl и сравнить ее. Когда вы найдете совпадение, запомните свой индекс и передайте его sl.Delete ()
Вы не спрашиваете об удалении строки. Вы уже знаете, как это сделать. Вы спрашиваете о поиске линии. Прочтите каждую строку, проанализируйте ее, найдите совпадающие строки. Какую часть ты не можешь делать?
Термин «индекс» в этом контексте относится к номеру строки элемента в списке. TStringLists основаны на 0, поэтому первая строка - 0, вторая - 1 и т. Д. Приведенные ниже ответы помогут вам и могут быть изменены в соответствии с вашими собственными обстоятельствами. Кроме того, имейте в виду, что существует ошибка, из-за которой разделители не работают правильно (конечно, в Delphi 7), не полагайтесь на нее. Не загружайте CSV-файл и полагайтесь на функции-разделители.
Итак, в вашем примере, вот так
Это может быть медленным для больших файлов, поскольку IndexOfName проходит цикл через каждую строку в TStringList и проверяет каждую строку по очереди, пока не найдет совпадение.
Отказ от ответственности: Протестировано / работает с Delphi 2007, Delphi 7 может отличаться.
Этот алгоритм работает, но после удаления строки в текстовом файле остается дополнительная открытая строка, что проблематично, когда дело доходит до заполнения массива пользователей из данных в текстовом файле. Есть ли способ выполнить этот алгоритм, но включить метод удаления лишней строки, оставшейся в текстовом файле?
@MarkvanHeerden: О какой дополнительной строке вы имеете в виду? Пожалуйста, будьте более конкретными. Delete() удаляет всю строку, она не записывается как пустая строка. Единственное возможное место, где я мог подумать, что появится пустая строка, - это самый конец файла, но SaveToFile() не следует писать туда пустую строку, если только последняя запись в списке не пуста с самого начала. Вы УВЕРЕНЫ, что в списке нет пустых записей?
Это не сработает, скажем, UserEmail или UserPassword . И он также не может удалить несколько элементов.
Я не понимаю, почему так много людей так усложняют задачу. Это довольно просто:
Как говорит Arioch'The, если вы сохраните файл с тем же именем, вы рискуете потерять свои данные в случае сбоя сохранения, поэтому вы можете сделать что-то вроде
Это сохраняет резервную копию исходного файла как FileName + '.old' .
Пояснения
Работаем в обратном направлении
Зачем работать в обратном направлении? Потому что, если у вас есть следующие предметы
И вы удалите элемент в ^ , тогда следующие элементы сместятся вниз:
Если вы выполните итерацию вперед, вы теперь укажете на
и E никогда не исследуется. Если вы вернетесь назад, вы укажете на:
Обратите внимание, что E , F и G уже были проверены, так что теперь вы действительно изучите следующий элемент C , и вы не пропустите ни одного. Кроме того, если вы пойдете вверх, используя 0 to Count - 1 , и delete, Count станет на единицу меньше, и в конце вы попытаетесь получить доступ за границу списка. Этого не может произойти, если вы работаете в обратном направлении, используя Count - 1 downto 0 .
Читайте также: