[Решено] Ошибка "File not open" при чтении файла

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

artem78
новенький
Сообщения: 50
Зарегистрирован: 09.08.2015 17:52:24

[Решено] Ошибка "File not open" при чтении файла

Сообщение artem78 »

Вроде бы тривиальная задача построчного чтения текстового файла.

Код: Выделить всё

procedure TForm1.Button1Click(Sender: TObject);
var
  F: TextFile;
  Line: String;
begin
  AssignFile(f, FileNameEdit1.FileName);
  try
    Reset(F);
    while not EOF(F) do
    begin
      ReadLn(F, Line);
      ParseLine(line);
    end;
  finally
    CloseFile(f);
  end;
end;  
Но есть одно большое НО: если в этот файл другая программа производит запись данных (файл является логом работы другой программы), то попытка чтения завершится ошибкой "File not open". Хотя тот же самый файл можно без проблем открыть, например, в Notepad++.

Как побороть?
Последний раз редактировалось artem78 23.02.2024 12:16:04, всего редактировалось 1 раз.
xchgeaxeax
постоялец
Сообщения: 198
Зарегистрирован: 11.05.2023 02:51:40

Сообщение xchgeaxeax »

Скорее всего дело в запрете на запись у файла лога т.к. он уже открыт.
Попробуйте читать файл через TFileStream или OpenText

ADD: И это не чтение завершается с ошибкой, а Reset. Добавьте проверку IOResult
artem78
новенький
Сообщения: 50
Зарегистрирован: 09.08.2015 17:52:24

Сообщение artem78 »

xchgeaxeax писал(а):Попробуйте читать файл через TFileStream
Насколько я понимаю, он для двоичных, а не текстовых файлов предназначен. Как с его помощью читать файл построчно мне не понятно.

Добавлено спустя 18 минут 30 секунд:
xchgeaxeax писал(а):OpenText
Что-то не могу найти, в каком юните это.
iskander
энтузиаст
Сообщения: 627
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

artem78 писал(а):Насколько я понимаю, он для двоичных, а не текстовых файлов предназначен. Как с его помощью читать файл построчно мне не понятно.
Посмотрите в сторону TFileReader из модуля StreamEx, который как раз предполагает построчное чтение текстового файла. Что-то вроде

Код: Выделить всё

...
  reader := TFileReader.Create(TextFileName, fmOpenRead or fmShareDenyNone, 4096);
  try
    while not reader.Eof do begin
      line := reader.ReadLine;
      ParseLine(line);
    end;
  finally
    reader.Free;
  end;  
...
xchgeaxeax
постоялец
Сообщения: 198
Зарегистрирован: 11.05.2023 02:51:40

Сообщение xchgeaxeax »

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

Код: Выделить всё

procedure ParseFile(const aFileName: String);
  function PostInc(var I: LongInt; N: LongInt = 1): LongInt; inline;
  begin
    Result := I;
    I := I + N;
  end;
  function ReadBuff(F: TFileStream; S: LongInt; var B): LongInt;
  begin
    Result := F.Size - F.Position;
    if Result > S then Result := S;
    F.ReadBuffer(B, Result);
  end;
var
  F: TFileStream;
  B: array [0 .. 511] of Char;
  C, I, L: LongInt;
  S: String;
begin
  F := TFileStream.Create(aFileName, fmOpenRead);
  S := '';
  L := 1;
  while F.Position < F.Size do begin
    I := 0;
    C := ReadBuff(F, Length(B), B);
    while I < C do begin
      while (I < C) and (B[I] <> #13) and (B[I] <> #10) do S := S + B[PostInc(I)];
      if I = C then continue;
      if I = 511 then begin
        B[0] := B[I];
        I := 0;
        C := ReadBuff(F, Length(B) - 1, B[1]);
      end;
      if B[I] = #13 then begin
        if B[I + 1] = #10 then inc(I, 2) else inc(I);
      end else if B[I] = #10 then begin
        if B[I + 1] = #13 then inc(I, 2) else inc(I);
      end;
      ParseString(PostInc(L), S);
      S := '';
    end;
  end;
  if Length(S) > 0 then ParseString(S);
  FreeAndNil(F);
end;
Добавлено спустя 59 секунд:
artem78 писал(а):Что-то не могу найти, в каком юните это.
Если у вас Lazarus, тогда это внутренняя функция, которую используют Reset/Rewrite/Append. Она позволяет поиграться с флагами.
Аватара пользователя
Alexander
энтузиаст
Сообщения: 864
Зарегистрирован: 18.12.2005 18:10:00
Откуда: оттуда
Контактная информация:

Сообщение Alexander »

Если только чтение, то в любом случае после AssignFile(f, FileNameEdit1.FileName); нужно добавить FileMode := 0;
Alex2013
долгожитель
Сообщения: 3211
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

artem78 писал(а):
xchgeaxeax писал(а):Попробуйте читать файл через TFileStream
Насколько я понимаю, он для двоичных, а не текстовых файлов предназначен. Как с его помощью читать файл построчно мне не понятно.
Так ясно же читайте "по байтово" и завешайте чение каждой строки по признаку eol (#13#10) для ускорения можно читать поблочно в буфер и парсить на предмет eol уже его.
Зы
Пример от xchgeaxeax скорее всего рабочий но (ИМХО) немного переусложнен.
Зы Зы
Но проблему возможной блокировки файла все это все равно не решает.
Если другая программа тоже написана вами то можно попробовать открывать и закрывать файл для записи каждой строки,а в программе читающей лог добавить чтение каждой строки "с конца" отлеживая увеличение размера файла + пару тройку попыток открытия и чтения.

Добавлено спустя 1 час 5 минут 17 секунд:
Алиса Яндексовна советует .
Можешь подсказать как читать последнюю строку в текстовом файле без перебора всего файла в Лазарус?
В Lazarus, вы можете использовать TStreamReader для чтения текстового файла построчно. Однако, у TStreamReader нет встроенной функции для чтения последней строки без перебора всего файла.
Один из способов сделать это - прочитать файл дважды: сначала прочитать все строки, чтобы узнать количество строк в файле, а затем прочитать последнюю строку, используя индекс этого количества минус единица. Это может быть не очень эффективно, если файл очень большой или если вам нужно читать последнюю строку часто.
Вот пример кода для чтения последней строки файла:
{ Объявляем переменные и инициализируем TFileStream }
var
Stream: TStreamReader;
LastLine: string;
I: Integer;
begin
{ Открываем файл }
Stream := TStreamReader.Create(TFileStream.Create('test.txt', fmOpenRead));
try
{ Считаем количество строк файла }
I := 0;
while Stream.ReadLine <> nil do
begin
Inc(I);
end;

{ Закрываем поток }
Stream.Close;
Хм! Разумеется нам при "индексировании" ( может пригодится при работе с действительно большим файлом) полезно получить не номер строки, а позицию ее начала в файле, но это уже совсем просто...
(С все-же "ИИ это такой ИИ" типичный "джин из сказки" . Спросишь - ответит... Но так что это скорее всего будет почти бесполезно! :wink: ...особенно доставил пассаж про "чтение дважды" (условие Stream.ReadLine <> nil тоже немного сомнительно (там строка на выходе... какой там к черту nil ? )... но что-то полезное в этой подсказки получить все-же можно
TStreamReader действительно существует... https://lazarus-ccr.sourceforge.io/docs ... eader.html )
Последний раз редактировалось Alex2013 23.02.2024 10:13:37, всего редактировалось 1 раз.
xchgeaxeax
постоялец
Сообщения: 198
Зарегистрирован: 11.05.2023 02:51:40

Сообщение xchgeaxeax »

Alex2013 писал(а):...особенно доставил пассаж про "чтение дважды" ...
С таким подходом Алесе до захвата мира далеко. Мощности не хватит столько лишних действий выполнять.

Сохранить последнюю (там появится nil при чтении файла) и предпоследнюю прочитанную строку - она не догадалась.
Alex2013
долгожитель
Сообщения: 3211
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

xchgeaxeax писал(а):Сохранить последнюю (там появится nil при чтении файла) и предпоследнюю прочитанную строку - она не догадалась.
По моему она просто не совсем точно интерпретировала вопрос... (результат работы предложенного кода, номер последней строки )
xchgeaxeax писал(а):С таким подходом Алесе до захвата мира далеко
Да "кутлухочка еще маленькая"... :wink: но это пока...
Последний раз редактировалось Alex2013 23.02.2024 10:54:07, всего редактировалось 1 раз.
xchgeaxeax
постоялец
Сообщения: 198
Зарегистрирован: 11.05.2023 02:51:40

Сообщение xchgeaxeax »

Попробовал использовать TStreamReader.

Код: Выделить всё

function ParseFile2(const aFileName: String): LongInt;
var
  F: TFileStream;
  R: TStreamReader;
begin
  Result := 1;
  F := TFileStream.Create(aFileName, fmOpenRead);
  R := TStreamReader.Create(F, 4096, False);
  while (not R.Eof) and (ParseLine(R.ReadLine, Result)) do inc(Result);
  FreeAndNil(R);
  FreeAndNil(F);
end;
Код действительно получился короче.
Последний раз редактировалось xchgeaxeax 23.02.2024 11:13:23, всего редактировалось 2 раза.
iskander
энтузиаст
Сообщения: 627
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

xchgeaxeax писал(а):Как по мне, то лучше использовать буфер для чтения и вручную собирать строки из файлового буфера, находя конец строки.
А что, пример с TFileReader нерабочий?
xchgeaxeax
постоялец
Сообщения: 198
Зарегистрирован: 11.05.2023 02:51:40

Сообщение xchgeaxeax »

Поправил на рабочий вариант. Надо было ориентироваться на R.Eof, а не на S.Position

Добавлено спустя 2 минуты 2 секунды:
iskander писал(а):А что, пример с TFileReader нерабочий?
Он менее универсален. Если данные только в файлах, тогда можно использовать его. А если надо взять данные из буфера в памяти? В TStreamReader можно передать TMemoryStream и все будет точно так же.
Alex2013
долгожитель
Сообщения: 3211
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

xchgeaxeax писал(а): В конце чтения файла произошла ошибка и последние 100 строк просто не прочитались.
Дык вестимо нужно "окавычить" R.ReadLine через проверку исключения try finally ... + перед каждым чтением провеять что F.Position <> F.Size. Да и IOResult возможно поверить лишним не будет.
Зы
Код действительно получился короче.
...

Код: Выделить всё

function ParseFile2(const aFileName: String): LongInt;
var
  F: TFileStream;
  R: TStreamReader;
begin
  Result := 1;
 try 
  F := TFileStream.Create(aFileName, fmOpenRead);
  R := TStreamReader.Create(F, 4096, False);
  while (not R.Eof) and (ParseLine(R.ReadLine, Result)) do inc(Result);
 finally
  FreeAndNil(R);
  FreeAndNil(F);
 end
end;
О вот так действительно более похоже на правду..
Последний раз редактировалось Alex2013 23.02.2024 11:31:01, всего редактировалось 4 раза.
iskander
энтузиаст
Сообщения: 627
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

xchgeaxeax писал(а):Он менее универсален. Если данные только в файлах, тогда можно использовать его. А если надо взять данные из буфера в памяти? В TStreamReader можно передать TMemoryStream и все будет точно так же.
А вопрос-то в топике был о чём?
artem78
новенький
Сообщения: 50
Зарегистрирован: 09.08.2015 17:52:24

Сообщение artem78 »

Alexander писал(а):Если только чтение, то в любом случае после AssignFile(f, FileNameEdit1.FileName); нужно добавить FileMode := 0;
Не помогло. Та же ошибка.
iskander писал(а):Посмотрите в сторону TFileReader из модуля StreamEx, который как раз предполагает построчное чтение текстового файла.
Зато этим способом с флагами fmOpenRead or fmShareDenyNone работает как надо. Большое спасибо!
xchgeaxeax писал(а):Понимаю, вручную работать с данными уже не феншуй.
А смысл "изобретать велосипед", когда "из коробки" уже есть то что нужно?
xchgeaxeax писал(а): iskander писал(а):
А что, пример с TFileReader нерабочий?


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