Edycja pliku zapisanego w formacie UTF-8 without BOM

0

Mam taki pliczek tekstowy ( https://www.mediafire.com/file/2mt9bkwbefa1mib/index.txt/file )
Chcę podmienić linię

<InputFilePath>\\XEON-PC\RipBot264temp\job1\video.mkv</InputFilePath>

i następnie zapisać.

Problem jest taki że nie jest to zwykłe kodowanie ANSI tylko UTF-8 without BOM. Jak wygląda procedura obróbki takich plików w takim np. TStringList?

Mam Delphi 10.3 oraz Delphi 7 z zainstalowanym komponentem TNT ( https://github.com/rofl0r/TntUnicode ).

1

Plik Utf8 należałoby wczytać do zmiennej string, przekonwertować do Ansii funkcją Utf8ToAnsi, dokonać potrzebnych edycji, ponownie przekonwertować do Utf8 funkcją AnsiToUtf8 i zapisać na dysk.

Ale ten plik (index.txt) jest kodowany w ANSI tyle, że zapisany w formacie UNIX (znaki końca linii to #10 zamiast #13#10)

0

Ciekawe dlaczego notepad++ wykrywa go jako UTF-8 without BOM?

0

Nie wiem, której wersji npp używasz. Ja zatrzymałem się na v7.5.9 ponieważ stwierdziłem, że nowsze nie radzą sobie dobrze z automatycznym wykrywaniem kodowania.
Jakieś dziwne znaki widzę na końcu pliku w sekcji ExtraDataList.

0
pelsta napisał(a):

Nie wiem, której wersji npp używasz. Ja zatrzymałem się na v7.5.9 ponieważ stwierdziłem, że nowsze nie radzą sobie dobrze z automatycznym wykrywaniem kodowania.

6.3

0

Te dziwne znaki muszą tam być! Gdy edytuje ten plik to one mi znikają!

Prosty przykład

StringList:=TStringList.Create;
  StringList.LoadFromFile('index.txt');
  StringList.SaveToFile('newindex.txt');

Oryginalny plik
title

nowy plik
title

Nie dość że "dziwne znaki" znikają to jeszcze brakuje paru lini na końcu. WTF?!

0

Znaki kontrolne zapisywane w plikach tekstowych powinny być escapowane, tak aby nie wpływały one na metody czytające ciągi znaków. A tu drugim znakiem jest NULL i do widzenia – się nie dziw że ucina.

0

Dobra. To w praktyce jak zapisać tak aby nic nie ucinało?

0

Escape character – poczytaj, znajdziesz tam dużo przykładów. Sprawdź też czy masz dostęp do metod, które pozwalają formatować takie ciągi (kodować i odkodowywać). Jeśli nie to będziesz musiał sobie takie napisać.

W razie czego zamiast escapowania, możesz konwertować ciąg na jakiś Base64 lub coś podobnego – byle ciąg wyjściowy nie zawierał surowych znaków kontrolnych.

0

Ja pierdziele! Tyle roboty aby podmienić jedną głupią linie w pliku tekstowym...

0

No jak się surowe NULLe pakuje do tekstowego pliku to nie dziwne, że API się sypie.

0
Atak_Snajpera napisał(a):

Ja pierdziele! Tyle roboty aby podmienić jedną głupią linie w pliku tekstowym...

Ja w takim wypadku, gdzie trzeba patchować plik binarny (a tak trzeba rozważyć ten plik), ładowałem pliczek do strumienia a nastepnie kopiowałem pamięć do AnsiStringa. Jak będziesz miał to w tej zmiennej to robisz sobie StringReplace i nastepnie zapisujesz do strumienia i do pliczku. Niestety nie poratuje Cię tym kodem, bo go już nie mam

0

Faktycznie. Plik należy potraktować jako binarny.

function ReadString(fn:string):string;
var
  f:file of Char;
  fs:Integer;
begin
  AssignFile(f,fn);
  Reset(f);
  fs:=FileSize(f);
  SetLength(Result,fs);
  BlockRead(f,Result[1],fs);
  CloseFile(f);
end;

procedure WriteString(fn,s:string);
var
  f:file of Char;
  fs:Integer;
begin
  AssignFile(f,fn);
  Rewrite(f);
  fs:=Length(s);
  BlockWrite(f,s[1],fs);
  CloseFile(f);
end;

...

var s1,s2:string;
  s1:=ReadString('index.txt');
  s2:=StringReplace(s1,'<InputFilePath>\\XEON-PC\RipBot264temp\job1\video.mkv</InputFilePath>','<InputFilePath>nowy wpis</InputFilePath>',[]);
  WriteString('index1.txt',s2);
0

Ostatecznie wczoraj poradziłem sobie z tym problemem w trochę bardziej skomplikowany sposób niż to zaproponował pelsta.

function TActivationThread.ModifyInputFilePathInIndexFile(InputFile:string):boolean;
var StringList:TStringList;
    SourceFileHandle,TargetFileHandle:THandle;
    SourceStream,TargetStream:TFileStream;
    DataArray:array of byte;
    PositionInFile,NullPos:int64;
    PositionInArray:integer;
    BytesRead,BytesWritten,x:integer;
    TextLine,CurrentDomainName,CurrentJob,OutputFile:string;

    function FindNullPos(var Buffer:array of byte):integer;
    var x:integer;
    begin
      Result:=-1;
      for x:=0 to Length(Buffer)-1 do
      begin
        if Buffer[x]=0 then
        begin
          Result:=x;
          exit;
        end;
      end;
    end;

begin

  Result:=false;
  if FileExists(InputFile)=false then exit;

  CurrentDomainName:=form1.JvComputerInfoEx1.Identification.LocalComputerName;
  CurrentJob:=Copy(CurrentJobFolder,Pos('job',CurrentJobFolder),MaxInt);
  OutputFile:=CurrentJobFolder+'\video.mkv.lwi.tmp';
  
  StringList:=TStringList.Create;
  StringList.LoadFromFile(InputFile);

  for x:=0 to StringList.Count-1 do
  begin
    TextLine:=StringList.Strings[x];
    if Pos('<InputFilePath>',TextLine)>0 then
    begin
      StringList.Strings[x]:='<InputFilePath>\\'+CurrentDomainName+'\RipBot264temp\'+CurrentJob+'\video.mkv</InputFilePath>';
      break;
    end;
  end;

  StringList.SaveToFile(OutputFile);
  StringList.Free;

  SourceFileHandle:=CreateFile(PChar(InputFile), GENERIC_READ, FILE_SHARE_READ, nil,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  TargetFileHandle:=CreateFile(PChar(OutputFile), GENERIC_WRITE, FILE_SHARE_WRITE, nil,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

  if SourceFileHandle=INVALID_HANDLE_VALUE then exit;
      
  SourceStream:=TFileStream.Create(SourceFileHandle);
  TargetStream:=TFileStream.Create(TargetFileHandle);
  TargetStream.Seek(0,soFromEnd);

  SetLength(DataArray,16777216);
  try
  
    repeat
      PositionInFile:=SourceStream.Position;
      BytesRead:=SourceStream.Read(DataArray[0],Length(DataArray));

      PositionInArray:=FindNullPos(DataArray);
      if PositionInArray>-1 then
      begin
        NullPos:=PositionInFile+PositionInArray;
        SourceStream.Seek(NullPos,soBeginning);

        repeat
          BytesRead:=SourceStream.Read(DataArray[0],Length(DataArray));
          BytesWritten:=TargetStream.Write(DataArray[0],BytesRead);
        until (BytesRead=0) or (BytesWritten=0);

      end;

    until (BytesRead=0) or (BytesWritten=0);

  finally
    SourceStream.Free;
    TargetStream.Free;

    CloseHandle(SourceFileHandle);
    CloseHandle(TargetFileHandle);
    
    Result:=CopyFile(PChar(OutputFile),PChar(CurrentJobFolder+'\video.mkv.lwi'),false);
    DeleteFile(OutputFile);
  end;
   
end;
0

Jakoś strasznie dużo kodu jak na tak prostą czynność

0

Tak sobie wczoraj wykombinowałem że jeśli TStringList mi ucina wszystko od bajtu 0 to sobie dokleję resztę strumieniami.

0
Atak_Snajpera napisał(a):

Tak sobie wczoraj wykombinowałem że jeśli TStringList mi ucina wszystko od bajtu 0 to sobie dokleję resztę strumieniami.

Proponuje użyć kodu @pelsta

0

@Atak_Snajpera a masz wpływ na format tego pliku? bo jest źle pomyślany.

0
Azarien napisał(a):

@Atak_Snajpera a masz wpływ na format tego pliku? bo jest źle pomyślany.

Nie, nie mam. Taki plik generuje indexer L-SMASH ( https://github.com/VFR-maniac/L-SMASH-Works/tree/master/AviSynth )

0

Załadowałem ten plik do tstringlist ale we freepascalu (lazarus) i wszystko działa ok, nie trzeba dodatkowych funkcji do formatowania stringów. Nic się nie usuwa:)

1 użytkowników online, w tym zalogowanych: 0, gości: 1