Как считать последнюю строку из файла

Обновлено: 05.07.2024

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

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

Используйте seekg для перехода к концу файла, затем верните его, пока не найдете первую новую строку. Ниже приведен пример кода с верхней части головы с помощью MSVC.

И ниже - тестовый файл. Он преуспевает в пустых, однострочных и многострочных данных в текстовом файле.

Перейдите к концу и начните чтение блоков назад, пока не найдете какие-либо критерии для линии. Если последний блок не "заканчивается" линией, вам, вероятно, также придется попробовать и сканировать вперед (при условии, что в активном приложении добавляется файл).

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

В то время как ответ derpface определенно корректен, он часто возвращает неожиданные результаты. Причина этого заключается в том, что, по крайней мере, на моей операционной системе (Mac OSX 10.9.5), многие текстовые редакторы завершают свои файлы символом "конечной линии" .

Например, когда я открываю vim, наберите только один символ "a" (нет возврата) и сохраните, теперь файл будет содержать (в шестнадцатеричном формате):

Где 61 - буква "a", а 0A - символ конца строки.

Это означает, что код derpface вернет пустую строку для всех файлов, созданных таким текстовым редактором.

Хотя я могу, конечно, представить случаи, когда файл, заканчивающийся "конечной строкой", должен возвращать пустую строку, я думаю, что игнорирование последнего символа "конечной строки" было бы более уместным при работе с обычными текстовыми файлами; если файл заканчивается символом "конечной линии" , мы должным образом игнорируем его, и если файл не заканчивается символом "конечной линии" , нам не нужно его проверять.


Как добаватить текст в последнюю строку текстового файла
пересмотрел много в Гугле, подскажите пожалуйста,как добавить текст в первую строку текстового.


Удалить последнюю строку из файла и посчитать количество символов в тексте
Прочитали текст из файла и теперь нужно удалить последнюю строку и посчитать количество символов в.


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

считать только последнюю строку из файла не обрабатывая другие
Можно как-то считать только последнюю строку из файла не обрабатывая другие. то-есть просто как-то.

Крок, да возможно. Только это "вручную" делать придётся. Открываете поток файла, перематываете указатель позиции чтения в самый конец потока, потом читаете посимвольно данные в обратном порядке, к началу, пока не попадётся символ переноса строки (CRLF или просто LF), это и будет начало последней строки. Можно будет или данные ранее прочитанные "развернуть" или заново всё прочитать, но уже в сторону конца файла. Ну и сразу же после перемотки вы можете видеть символ переноса строки, так как последняя строка таким символом тоже может заканчиваться, его нужно будет проигнорировать. Крок, а string s = File.ReadAllLines("").Last(); Вам не подойдет? Или же Вам нужна именно последняя строка файла, без загрузки его в память?

Shogun31337, я вот прямо ожидал, что кто-нибудь такое предложит. :)

Usaga, ну дык самое очевидное решение. :)

Количества слез, пролитого нормальными программистами при работе с чужим кодом, состоящим только из "очевидных"
и, находящихся на первых строках результата поиска решений, хватит на over9000 всемирных потопов :(
Мне нужно в строку считать только последнюю строку. Количества слез, пролитого нормальными программистами при работе с чужим кодом, состоящим только из "очевидных" и, находящихся на первых строках результата поиска решений, хватит на over9000 всемирных потопов Woldemar89, можно поинтересоваться чем по Вашему мнению плох ReadAllLines? Если не планируется считывать гигабайтные файлы, то данное решение, на мой взгляд, более чем оправдано. Или Вы вообще про "очевидные решения"?
Хотя, немного исправлю себя - лучше, в данном случае, использовать ReadLines(), а не ReadAllLines().

Добавлено через 2 минуты

Aumi, что то Вы совсем перемудрили! :)

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

А я ТЗ буквально понимаю :)

Вообще, - потому что брать и применять 1й попавшийся\пришедший в голову способ, не обозрев другие - плохой подход.

Хотя, немного исправлю себя - лучше, в данном случае, использовать ReadLines(), а не ReadAllLines(). Да можно, но, тогда уж и я поправлю :)
ReadLines().Reverse() - возможно да, - это будет оптимально, однако я не искал инфы по перфомансу этой шняги (а вдруг там *опа) и решил положится на ответ авторитета (Jon Skeet) А если тупо прочитать файл построчно в какой-нибудь список и тупо взять последний элемент ? А если тупо прочитать файл построчно в какой-нибудь список и тупо взять последний элемент ? Вообще, - потому что брать и применять 1й попавшийся\пришедший в голову способ, не обозрев другие - плохой подход.

Согласен на 1000%!

А вот тут не понял. Зачем реверс, если Last() берет последнюю строку? Я, конечно, не уверен, но, думаю, что пройти по коллекции вперед до конца быстрее, нежели реверсить ее и потом брать первый элемент. А вот тут не понял. Зачем реверс, если Last() берет последнюю строку? Я, конечно, не уверен, но, думаю, что пройти по коллекции вперед до конца быстрее, нежели реверсить ее и потом брать первый элемент.

Тут Вы правы, хоть и не уверены :) - эта дрянь еще и у меня с OutOfMemory вылетает.
При использовании Reverse потребление памяти растет астрономически.
Last на реально мощном ноуте выдает посл. строку 1,5ГБ файла за 10 сек при стабильно малом потреблении памяти около 14мб
(стоит ссд, но тестил на втором 5400rpm hdd)

По поводу Reverse - это было "логичное" предположение идти с конца, но я не в курсе был что Reverse отожрет память.
О чем собственно, подстраховался.

ReadLines().Reverse() - возможно да, - это будет оптимально, однако я не искал инфы по перфомансу этой шняги (а вдруг там *опа) и решил положится на ответ авторитета (Jon Skeet)

В итоге:
Конкретно для этой задачи, - наверняка пофигу(сразу понятно было), что юзать, для других - 10 и более секунд может быть критично (да и там скорее БД будет лучше).
Так же можно проверить перфоманс MiscUtils.

Никогда не рассматривайте IEnumerable<> как коллекцию, не забывайте про отложенное выполнение
Также в коде выше можете посмотреть время создания запросов.
Еще, например для List<> Last() имеет сложность O(1) тк там будет выполнено
когда в данном случае все совсем не так и очевидно сложность будет O(n).

ну я тоже не был уверен, просто по логике, иначе и быть не должно.

Да, это понятно. Про "пройти" я упрощенно сказал с мыслью о том, что все дискутирующие стороны в курсе. :)

Никогда не рассматривайте IEnumerable<> как коллекцию, не забывайте про отложенное выполнение

Да, про отложенное выполнение я знаю (собственно, поэтому я и поправился, предложив использовать ReadLines, а не ReadAllLines). Просто IEnumerable все таки входит в пространство имен Collection. Так что, думаю, со спокойной душой можно называть это коллекцией. Да и, опять же, по логике причислить его к какому то иному типу тоже язык не поворачивается. Коллекция, просто со своими нюансами. Впрочем, как и любые другие типы коллекций.

Читал на одном форуме тоже спор по поводу использования разных методов для чтения из файла. Там стояла задача сосчитать количество строк. Было предложено 3 варианта: 1. ReadLines, 2. побайтовое чтение из файла и проверка на символ переноса строки. 3. А вот третий не помню. В общем, смысл в том, что один из спорящих заморочился и провел тестирование этих методов. По итогу оказалось, что ReadLines самый быстрый, хотя памяти при этом потребляет значительно больше. Побайтовое чтение, соответственно, самым медленным, но память практически не расходовал. Точные цифры сказать не могу, тупо не помню. Читал на одном форуме тоже спор по поводу использования разных методов для чтения из файла. Там стояла задача сосчитать количество строк. Было предложено 3 варианта: 1. ReadLines, 2. побайтовое чтение из файла и проверка на символ переноса строки. 3. А вот третий не помню. В общем, смысл в том, что один из спорящих заморочился и провел тестирование этих методов. По итогу оказалось, что ReadLines самый быстрый, хотя памяти при этом потребляет значительно больше. Побайтовое чтение, соответственно, самым медленным, но память практически не расходовал. Точные цифры сказать не могу, тупо не помню.

Я бы не стал доверять "тестам" человека, для которого не очевидно, что вычитывать весь файл в массив объектов-строк для того, чтобы просто взять последнюю - это норм.

Читать содержимое файла тоже можно по-разному.

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

Shogun31337, ваш вариант хорош только тем, что краток. Но он ниразу не эффективен, только бездумен. Такое можно использовать только в личных целях и молча, чтобы никто не видел, но советовать другим - откровенно вредить. Нуб не может отличить где что, хрень вы посоветовали или нет. Он возьмёт тот совет, что выглядит проще. Это потом уже, ему на собеседовании скажут, что он на фиг не нужен в роли программиста, когда начнёт демонстрировать подобные "навыки".











Здесь хотелось бы заметить, что проблема с кодировками будет при написании скрипта, на любом из родных для системы языков - cmd, vbscript, jscript.

Можно только сожалеть об отсутствии и решить эту проблему одним из следующих способов:
1. установить один из пакетов, содержащих достаточный набор команд, чтобы быть также уверенным как в командной строке unix, например, UnixUtils, Cygwin, GnuWin32
Иногда это не возможен из-за политик компаний, запрещающих самостоятельно устанавливать дополнительное ПО.
2. использовать PowerShell
Хотя все идет к тому, что PowerShell будет предустановлен на всех Windows, однако еще много систем на которых его нет. А установить нельзя.
3. написать собственный скрипт
Это уже выход, если 1 и 2 противопоказаны. Хотя есть свои недостатки (например упомянутая проблема - кодировки).

Я решил попробовать (частично или полностью) эмулировать работу некоторых важных команд обработки текстовых файлов средствами самой командной строки. Just for fun!
.
1. Количество строк в файле
Оказывается почти точно определить количество строк в файле очень просто:

И вот пример. Данная команда выводит немного дополнительной информации

Сказав "почти точно", я имел в виду, то что иногда результаты отличаются от видимого. Но этим грешат многие команды. Например, wc -l отбрасывает последнюю строку, если она не заканчивается переводом строки. В случае с find /c /v "" наблюдается расхождение с действительностью, если в конце файла несколько (более 2) пустых строк. По-видимому разные программы работают по разному - одни считают количество строк, другие количество переводов строк. И вследствие этого они могут различаться. Поэтому я рекомендую использовать следующее решение.

Команда выведет только размер файла в строках. И это самый точный способ узнать количество строк в файле.

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

.
2. Прочитать n последних строк в файле. Эмуляция команды tail
Оказывается, это очень простая задача:
1. узнать N - количество строк в файле;
2. вывести n строк, пропустив N-n:

.
Код на PowerShell:

.
3. Вывод нескольких начальных строк файла. Эмуляция команды head
Этот эмулятор я написал первым, хотя он не самый простой в реализации. Возможно, существуют варианты лучше, но, к сожалению, я их не нашел и, поэтому, я использовал "хак" с временным файлом. Суть его в том, что вначале создается временный файл, который содержит n-ое количество следующих строк:

то есть все номера требуемых строк.

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

.
И на последок, традиционно, код на PowerShell:

.
Послесловие
Здесь я привел только заготовки, но уже рабочие заготовки, которые частично решают проблему отсутствия полезных команд. Для полноценной эмуляции можно предусмотреть работу не только с заданным файлом или группой файлов, но и с консолью, и реализовать конвейерную обработку. Также можно было бы добавить проверку установки PowerShell в системе и, если он есть, выполнять его команды.

хеад и тайл в одном флаконе

smaharbA, как во флаконе дело обстоит с пустыми строками в файле?

  • Rumata
  • Разработчик
  • Неактивен











smaharbA
alexii
Коллеги. Во флаконе только head. Там нет tail.

есть там тайл - смотрите внимательно

alexii - не знаю, должно быть вроде как нормально, поправьте

smaharbA, я имел в виду, что разбор «for /f» пропускает пустые строки.

  • Rumata
  • Разработчик
  • Неактивен











Согласен. Но это не совсем то. В отличие от tail, more +N пропускает N строк из потока. И результат совершенно не предсказуем для потока в конвейере.

  • Rumata
  • Разработчик
  • Неактивен











разбор «for /f» пропускает пустые строки

Но вот посмотрите на этот пример:

  • greg zakharov
  • Разработчик
  • Неактивен

Код на PowerShell:
echo (get-content -path %windir%.

Слишком сложно, не находите?

А если нужно передать команду PoSh, то будет примерно так:

  • blackfox.blacktail
  • Участник
  • Неактивен
  • Рейтинг : [ 0 | 0 ]

Добрый день.
Использовал пример для подсчета количества строк в файле:

13 Ответ от smaharbA 2013-09-01 10:37:46

чем не устраивает ?

14 Ответ от blackfox.blacktail 2013-09-03 10:22:45

  • blackfox.blacktail
  • Участник
  • Неактивен
  • Рейтинг : [ 0 | 0 ]

2smaharbA
Если есть пустая строка, с символом SUB, её тоже считает.
Проще избавиться от этого символа, что я и сделал.
(При использовании copy, нужно поставить ключ /B)

15 Ответ от greg zakharov 2013-09-03 15:52:47

  • greg zakharov
  • Разработчик
  • Неактивен

EQU

Если есть пустая строка, с символом SUB, её тоже считает.

На оном спотыкается и Ruby (версия старая, до установки последней дело никак не доходит).

16 Ответ от smaharbA 2013-09-03 17:27:15

17 Ответ от greg zakharov 2013-10-13 10:27:59 (изменено: greg zakharov, 2013-10-13 10:28:42)

  • greg zakharov
  • Разработчик
  • Неактивен

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

head без временных файлов.

Может быть, но сколько бы не пробовал на выходе одинаковый результат. Может у меня на Win2k3 find специфический?

18 Ответ от Мальчик-гей 2013-10-17 09:04:37

  • Мальчик-гей
  • Участник
  • Неактивен

Может быть, но сколько бы не пробовал на выходе одинаковый результат.

Коллега smaharbA прав:

19 Ответ от greg zakharov 2013-10-17 11:51:48

  • greg zakharov
  • Разработчик
  • Неактивен

Смысл то один и тот же - количество строк в файле. А отбрасывать из вывода имя файла - дело вкуса. Так что это скорее "не равноценно", нежели "не эквивалентно". Или Вы русский язык как-то на свой лад интерпретируете?

20 Ответ от Rumata 2013-10-17 13:17:45

  • Rumata
  • Разработчик
  • Неактивен











greg zakharov
В данном случае smaharbA прав. Результат обеих команд одинаковый, но вывод - разный. Это как в JavaScript

21 Ответ от Мальчик-гей 2013-10-21 13:38:14

  • Мальчик-гей
  • Участник
  • Неактивен

Так что это скорее "не равноценно", нежели "не эквивалентно". Или Вы русский язык как-то на свой лад интерпретируете?

Дорогой коллега! Я понимаю эквивалентность вполне академически, как отношение, обладающее свойствами рефлексивности, симметричности и транзитивности. И в этом смысле на одном и том же классе объектов могут быть заданы различные отношения эквивалентности, так что объекты эквивалентные в одном отношении не будут эквивалентны в другом. Вспомните хотя бы конгруэнтность и подобие геометрических фигур. В рамках же этой темы требовалась более сильная эквивалентность, с совпадением формата вывода результатов, так как всё началось с

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

чему smaharbA и предложил эквивалентную именно в этом отношении замену.
По поводу "равноценно" и "эквивалентно" советую заглянуть в словарь, а ещё лучше, попробовать самому догадаться как же слово "эквивалентно" переводится на русский.

22 Ответ от greg zakharov 2013-10-21 15:07:13 (изменено: greg zakharov, 2013-10-21 16:40:40)

  • greg zakharov
  • Разработчик
  • Неактивен

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

Простите, у Вас филологическое образование? Или Вы страдаете дислексией, что не вполне можете разуметь написанное? Тем паче, что

Я понимаю эквивалентность вполне академически, как отношение, обладающее свойствами рефлексивности, симметричности и транзитивности.

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

undefined == null // true
undefined === null // false

Не приплетайте сюда, пожалуйста, javascript. null и undefined - это два тривиальных типа данных, каждый из которых определяет только одно значение. Однако, заявлять об их эквивалентности, как-то однако. Считается, что у значение null - объектный тип. При всем этом значение null уникально и отличается от любых других. Если переменная равна null, значит в ней не содержится допустимого объекта (массива, числа, строки и т.д.) undefined же возвращается же либо при обращении к переменной, которй никогда не присваивалось значение, либо к свойству объекта которого не существует. При этом null и undefined не эквивалентны друг другу, несмотря на то, что оператор эквивалентности считает их равными, - это написано в любой книге по javascript. Это можно также утверждать и потому, что в отличии от null значение undefined не является зарезервированным словом. Если будет время, почитайте на досуге, например, Дэвида Флэнегана, если точно помню и не делайте скоропалительных выводов.
Резюмирую, несмотря на общую схожесть многих языков, абсолютизировать понятие эквивалентности не стоит, - если не каждый, то некоторые из языков вкладывают собственное понятие в "эквивалентность", причем будь то естественный язык или язык программирования. То, что выдает в командном языке одинаковый результат, но лишь с некоторым "мусором", знаете ли еще не повод утверждать что заяц - это волк и вообще не млекопитающее, а аргументы г-на Мальчик-гей не убедительны, ибо таким образом можно доказать все что угодно, в том числе что и Ленин был грибом.

Переход в бесконечный цикл.

  • Перейдите к концу файла.
  • уменьшить позицию указателя на 1 и прочитать символ персонаж.
  • выйти, если мы найдем наши 10 строк или дойдем до начала файла.
  • теперь я буду сканировать полный файл до EOF и печатать их//не реализован в коде.

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

PS:
1. работа над окнами в блокноте ++

Это не домашнее задание

Также я хочу сделать это, не используя больше памяти или использования STL.

Я тренируюсь, чтобы улучшить свои базовые знания, поэтому, пожалуйста, не сообщайте о каких-либо функциях (например, tail -5 tc.)

пожалуйста, помогите улучшить мой код.

ОТВЕТЫ

Ответ 1

С вашим кодом возникает ряд проблем. Большинство важно то, что вы никогда не проверяете, что любая из функций удалось. И сохранение результатов ftell в int не является тоже очень хорошая идея. Тогда существует тест pos < begin ; это может произойти только в случае ошибки. И тот факт, что вы помещаете результаты fgetc в char (что приводит к в случае потери информации). И тот факт, что первый читал вас do находится в конце файла, поэтому произойдет сбой (и как только поток войдет в состояние ошибки, оно остается там). И тот факт, что вы не можете надежно выполнить арифметику по значениям, возвращаемым ftell (за исключением под Unix), если файл был открыт в текстовом режиме.

О, и нет "символа EOF"; 'ÿ' - совершенно character (0xFF в латинском-1). Как только вы присвоите возвращаемое значение от fgetc до char , вы потеряли возможность проверить конец файла.

Я мог бы добавить, что чтение назад по одному символу за раз крайне неэффективен. Обычным решением было бы выделить достаточно большой буфер, затем подсчитайте '\n' в нем.

Просто немного кода, чтобы дать идею:

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

Кроме того, это чисто Windows, и он предполагает, что фактическое файл содержит чистый текст и не содержит '\r' , который не являются частью CRLF. (Для Unix просто снимите последняя строка.)

Ответ 2

Комментарии в коде

Ответ 3

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

Пожалуйста, оставьте отзыв.

Ответ 4

Я считаю, вы используете fseek неправильно. Проверьте man fseek на Google.

Также вы должны установить положение в начале последнего элемента:

Вам не нужна переменная end .

Вы должны проверить возвращаемые значения всех функций ( fgetc , fseek и ftell ). Это хорошая практика. Я не знаю, будет ли этот код работать с пустыми файлами или аналогичным.

Ответ 5

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

Ответ 6

Использование: fseek(f1,-2,SEEK_CUR); назад

Я пишу этот код, он может работать, вы можете попробовать:

Ответ 7

Я бы использовал два потока для печати последних n строк файла: Это выполняется в O (строки) времени выполнения и O (строки).

Решение с O (строками) runtime и O (N) пространство использует очередь:

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