1

テキストファイルから読み取り、解析し、意味のある情報を抽出し、その情報を使用してSQLクエリを実行し、応答、出力ファイルを生成する必要がある特定のシナリオに取り組んでいます。

約 3000 行のコードがあります。すべてが期待どおりに機能しています。しかし、私は私のプロジェクトを混乱させる可能性のある難問を考えていました。

読み込まれるテキスト ファイル (Text.txt と呼びましょう) は、1 行または複数行で構成されます。

私の場合、「行」はそのセグメント名によって識別されます。たとえば、ISA、BHT、HB、NM1 などです。各セグメントの末尾は、特殊文字「~」によって識別されます。

ファイルが複数の行で構成されている場合 (各行が 1 つのセグメントに対応するように); いう:-

いさ……~

NM1.......~

ダメージ……〜

SE........〜

など....次に、私のコードは基本的に各「行」(つまり各セグメント)を一度に1つずつ読み取り、次のコマンドを使用して一時バッファーに保存します:-

         ReadLn(myFile,buffer);

次に、各行に基づいて評価を実行します。目的の出力を生成します。問題はありません。


ただし、問題は...ファイルが次のように表される単一行(複数のセグメントで構成される)のみで構成される場合はどうなりますか?

ISA.......~NM1.......~DMG.......~SE........~

次に、ReadLine コマンドを使用して、各セグメントではなく行全体を一度に 1 つずつ読み取ります。これは私のコードでは機能しません。

if、elseステートメントのペアを作成することを考えていました...これは、Txt.txtファイルで構成される行数に基づいています..など:-

行 = 1 の場合:- 各セグメントを一度に抽出します...特殊文字 '~' で区切られて、必要なタスクを実行します (3000 行のコード) それ以外の場合 行 > 1:- その後、一度に各行を抽出します (各セグメント) 必要なタスク (3000 行のコード) を実行します。

現在、3000 行のコードが 2 回繰り返されており、そのすべてのコードを 2 回コピーして貼り付けるのはエレガントではありません。

1 行のファイルまたは複数行のファイルに関係なく、評価に進むときに一度に 1 つのセグメントのみを使用するように、この問題を解決する方法についてフィードバックをいただければ幸いです。

4

2 に答える 2

1

これを行うには多くの方法があります。どちらが最適かは、これらのファイルの長さとパフォーマンスの重要性によって異なります。

簡単な解決策は、チルダ区切り文字に到達するまで一度に 1 文字ずつ読み取ることです。以下のルーチン ReadOneItem は、これを行う方法を示しています。

procedure TForm1.Button1Click(Sender: TObject);
const
  FileName = 'c:\kuiper\test2.txt';
var
  MyFile : textfile;
  Buffer : string;

  // Read one item from text file MyFile.
  // Load characters one at a time.
  // Ignore CR and LF characters
  // Stop reading at end-of-file, or when a '~' is read

  function ReadOneItem : string;
  var
    C : char;
  begin
    Result := '';

    // loop continues until break
    while true do
      begin

        // are we at the end-of-file? If so we're done
        if eof(MyFile) then
          break;

        // read in the next character
        read ( MyFile, C );

        // ignore CR and LF
        if ( C = #13 ) or ( C = #10 ) then
          {do nothing}
        else
          begin

            // add the character to the end
            Result := Result + C;

            // if this is the delimiter then stop reading
            if C = '~' then
              break;
          end;
      end;
  end;


begin
  assignfile ( MyFile, FileName );
  reset ( MyFile );
  try

    while not EOF(MyFile) do
      begin
        Buffer := ReadOneItem;
        Memo1 . Lines . Add ( Buffer );
      end;

  finally
    closefile ( MyFile );
  end;
end;
于 2013-01-03T09:41:46.727 に答える
0

Win32 APICreateFileMapping()MapViewOfFile()関数を介してファイル マッピングを使用し、生データをそのまま解析して、~文字をスキャンし、各セグメント間で発生する可能性のある改行を無視します。例えば:

var
  hFile: THandle;
  hMapping: THandle;
  pView: Pointer;
  FileSize, I: DWORD;
  pSegmentStart, pSegmentEnd: PAnsiChar;
  sSegment: AnsiString;
begin
  hFile := CreateFile('Path\To\Text.txt', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
  if hFile = INVALID_HANDLE_VALUE then RaiseLastOSError;
  try
    FileSize := GetFileSize(hFile, nil);
    if FileSize = INVALID_FILE_SIZE then RaiseLastOSError;
    if FileSize > 0 then
    begin
      hMapping := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, FileSize, nil);
      if hMapping = 0 then RaiseLastOSError;
      try
        pView := MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, FileSize);
        if pView = nil then RaiseLastOSError;
        try
          pSegmentStart := PAnsiChar(pView);
          pSegmentEnd := pSegmentStart;
          I := 0;
          while I < FileSize do
          begin
            if pSegmentEnd^ = '~' then
            begin
              SetString(sSegment, pSegmentStart, Integer(pSegmentEnd-pSegmentStart));
              // use sSegment as needed...
              pSegmentStart := pSegmentEnd + 1;
              Inc(I);
              while (I < FileSize) and (pSegmentStart^ in [#13, #10]) do
              begin
                Inc(pSegmentStart);
                Inc(I);
              end;
              pSegmentEnd := pSegmentStart;
            end else
            begin
              Inc(pSegmentEnd);
              Inc(I);
            end;
          end;
          if pSegmentEnd > pSegmentStart then
          begin
            SetString(sSegment, pSegmentStart, Integer(pSegmentEnd-pSegmentStart));
            // use sSegment as needed...
          end;
        finally
          UnmapViewOfFile(pView);
        end;
      finally
        CloseHandle(hMapping);
      end;
    end;
  finally
    CloseHandle(hFile);
  end;
于 2013-01-03T00:44:33.087 に答える