Как я могу получить PChar To Go за прошедшие шестнадцатеричные коды, чтобы добраться до конца файла в Delphi? - PullRequest
2 голосов
/ 11 февраля 2012

Я анализирую очень большие файлы (Unicode - Delphi 2009), и у меня есть очень эффективная процедура для этого с использованием переменных PChar, как описано в вопросе Stackoverflow: Какой самый быстрый способ анализа строки в Delphi?

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

Однако, когда вы загружаете файл, например:

FileStream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
Size := FileStream.Size;

, вы обнаруживаете, что размерфайл намного больше.Если вы открываете файл с помощью Блокнота, он загружается в конец файла, не останавливаясь на первом шестнадцатеричном: 00, как это делает PChar.

Как я могу прочитать до конца файла, все еще используя PCharРазбор, не замедляя мое чтение / анализ слишком много?

Ответы [ 3 ]

5 голосов
/ 11 февраля 2012

Принятый код в вашем другом вопросе вспыхивает, когда он достигает символа # 0.Чтобы это исправить, вам просто нужно сохранить длину ввода и проверить это вместо этого.Обновленный код будет выглядеть примерно так:

type
  TLexer = class
  private
    FData: string;
    FTokenStart: PChar;
    FCurrPos: PChar;
    FEndPos: PChar;                                         // << New
    function GetCurrentToken: string;
  public
    constructor Create(const AData: string);
    function GetNextToken: Boolean;
    property CurrentToken: string read GetCurrentToken;
  end;

{ TLexer }

constructor TLexer.Create(const AData: string);
begin
  FData := AData;
  FCurrPos := PChar(FData);
  FEndPos := FCurrPos + Length(AData);                      // << New
end;

function TLexer.GetCurrentToken: string;
begin
  SetString(Result, FTokenStart, FCurrPos - FTokenStart);
end;

function TLexer.GetNextToken: Boolean;
var
  cp: PChar;
begin
  cp := FCurrPos; // copy to local to permit register allocation

  // skip whitespace
  while (cp <> FEndPos) and (cp^ <= #32) do                 // << Changed
    Inc(cp);

  // terminate at end of input
  Result := cp <> FEndPos;                                  // << Changed

  if Result then
  begin
    FTokenStart := cp;
    Inc(cp);
    while (cp <> FEndPos) and (cp^ > #32) do                // << Changed
      Inc(cp);
  end;

  FCurrPos := cp;
end;
2 голосов
/ 11 февраля 2012

Если вы наберете символ #0, но не использовали все символы в файле, продолжайте. То, как вы продолжаете, зависит от того, как вы решили остановиться в первую очередь.

Вопрос, на который вы ссылались, имеет следующий код:

while (cp^ > #0) and (cp^ <= #32) do
  Inc(cp);

// using null terminator for end of file
Result := cp^ <> #0;

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

nChars := Length(FData);
nCharsSeen := 0;
while (nCharsSeen < nChars) and (cp^ <= #32) do begin
  Inc(cp);
  Inc(nCharsSeen);
end;

// using character count for end of file
Result := nCharsSeen < nChars;

Ссылочный ответ разбирал строку, поэтому я использовал Length дляузнать количество символов.Если вы анализируете файл, используйте вместо него что-то вроде TFileStream.Size.

1 голос
/ 11 февраля 2012

Я взял код из вашего ранее принятого ответа и немного изменил его, добавив две дополнительные переменные:

FPosInt: NativeUInt;
FSize: NativeUInt;

FSize инициализируется с длиной строки в конструкторе (длина переменной строки хранится в то время как PChar нет). FPosInt - номер текущего символа в вашем файле. Дополнительный код в конструкторе:

FSize := Length(FData);
FPosInt := 0;

Соответствующая часть в функции GetNextToken больше не останавливается на первом нулевом байте, но продолжается до тех пор, пока не будет достигнут последний символ строки:

// skip whitespace; this test could be converted to an unsigned int
// subtraction and compare for only a single branch
while (cp^ <= #32) and (FPosInt < FSize) do
  begin
  Inc(cp);
  Inc(FPosInt);
  end;

// end of file is reached if the position counter has reached the filesize
Result := FPosInt < FSize;

Я переключил два оператора в условии while, так как они оцениваются слева направо, и первый из них чаще всего оценивается как ложный.


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

FSize := Length(FData);
FStartPos := NativeUInt(FCurrPos);

А в GetNextToken:

// skip whitespace; this test could be converted to an unsigned int
// subtraction and compare for only a single branch
while (cp^ <= #32) and ((NativeUInt(cp) - FStartPos) < FSize) do
  Inc(cp);

// end of file is reached if the position counter has reached the filesize
Result := (NativeUInt(cp) - FStartPos) < FSize;
...