テキストファイルを読み込むには?

解決


Zungige  2005-07-05 19:26:02  No: 16177  IP: [192.*.*.*]

1レコードが20のフィールドでできたテキストファイルがあります。区切りは複数のスペース、タブ、複数の改行が入り混じっています。これを見やすくグリッドに表示するにはどうすればいいのですか?
CSVならStringList.CommaTextで簡単にできるのですが・・

編集    削除
deldel  2005-07-06 00:09:57  No: 16178  IP: [192.*.*.*]

var
  sl: TStringList;
  iw: Word;
begin
  sl := TStringList.Create;
  try
    sl.LoadFromFile('C:\aaa.txt');

    for iw := 0 to Pred(sl.Count) do begin
      StringGrid1.Rows[iw].CommaText := sl[iw];
    end;
  finally
    sl.Free;
  end;
end;

編集    削除
Zungige  2005-07-06 02:40:03  No: 16179  IP: [192.*.*.*]

deldel様ありがとうございました。

1レコードの中に複数の改行が入っていますので、StringGridでは1レコードが複数行に表示されます。1レコードを1行に表示するには改行コードをスペースに置き換える処理が必要なのでしょうか?

編集    削除
deldel  2005-07-06 02:56:50  No: 16180  IP: [192.*.*.*]

あ〜改行があるんですか・・・(って、書いてましたね^^;)

そうですね、string変数に1レコード分のデータを格納し、あとは 、
  S := StringReplace(S, #13, ',', [rfReplaceAll]);
でいいと思います。

編集    削除
Zungige  2005-07-06 18:35:29  No: 16181  IP: [192.*.*.*]

StringListでは#13を読み込めそうもありませんので、墓場のように避けている(^^)TFileStreamあたりを勉強したいと思います。初歩的な質問にもかかわらず、お付き合い下さいましたdeldel様に無限の感謝を捧げます。

編集    削除
3K  2005-07-07 01:16:13  No: 16182  IP: [192.*.*.*]

>>1レコードが20のフィールドでできたテキストファイル

20フィールド固定なら、レコードを無視して単純に20個読んだら
StringGrid.row + 1 すればよいのでは?
そんな単純な事ではないかなー?

編集    削除
deldel  2005-07-07 02:15:06  No: 16183  IP: [192.*.*.*]

あと、テキストファイルなのに1レコードに改行があるというのは、
どんな形なのでしょうか?なんかピンと来ないのですが、きっと
3Kさんのようにすればいいような気がします。

編集    削除
ん?  2005-07-07 05:29:44  No: 16184  IP: [192.*.*.*]

deldelさんの仰るとおり、データの一部を見せてもらえたら、また違った回答もつくのではないでしょうか?
危ないトコは、適当にマスクかけておけばよろしいかと。

ちなみに、ここで私が最後にレスしたTStringListExなんぞは、使えないか?
https://www.petitmonte.com/bbs/answers?question_id=2891

もっとも、ダブルクォーテションで囲まれていないデータの改行は、無視されますがね。

編集    削除
Zungige  2005-07-07 22:19:15  No: 16185  IP: [192.*.*.*]

多くの方々の手を煩わせて恐縮しております(m m)

MSDE2000で
osql -U sa -d Mydatabase -q "DBCC SHOWCONTIG WITH TABLERESULTS,ALL_INDEXES"
           -o "c:\Showcontig.txt"
のようにDBCC SHOWCONTIG  を -oスイッチをつけて実行して得たtxtファイルです。
2レコード分がこれです↓
-------------------------------
 sysobjects                                                                    
                                                                1 
  ncsysobjects2                                                          
                                                                       3 
            0           1                    196                 9 
                  9                      9.0                      0 
            1              0                   5352.0 
        33.876945495605469                    100.0           1 
            1                      0.0                      0.0 
 sysindexes                                                                    
                                                                2 
  sysindexes                                                             
                                                                       1 
            0          12                    319               110 
                458       205.22800000000001                      0 
            7              9          2563.8330078125 
         68.32427978515625                     20.0           2 
           10       41.666667938232422       42.857143402099609 
--------------------------------
sl := TStringList.Create;
sl.LoadFromFile('c:\showcontig.txt');
showmessage('count='+inttostr(sl.Count));
を実行しますとcount=18を表示します。18・・・・?
StringListで読み込むこと自体に無理があります?

編集    削除
anone  2005-07-07 23:18:29  No: 16186  IP: [192.*.*.*]

行の途中にはレコードの区切りがないのですね。そして、各フィールドは半角文字だけで出来ていると。

その場合は、TStringList でファイルを読み込んで処理するのは簡単です。

ここでは、TStringList で読み込むかわりに Memo1 に上のデータをコピペしました。
Memo1.Lines と TStringList は同型ですから同じように処理できます。

あらかじめ StringGrid1 を Form1 に貼り付けて置いてください。

procedure TForm1.FormCreate(Sender: TObject);
begin
  with StringGrid1 do
  begin
    FixedCols := 0;
    FixedRows := 0;
    RowCount := 1;
    ColCount := 20;
  end;
end;

procedure ExtractData(const str: string; sl:TStringList);
const
  Delim: set of Char = [' ', #9];
var
  i, start: integer;
  state: Boolean;
begin
  state := false;
  for i := 1 to length(str) do
  begin
    if (str[i] in Delim) then
    begin
      if state then
      begin
        sl.Add(Copy(str,start,i-start));
        state := false;
      end;
    end
    else
    begin
      if not state then
      begin
        start := i;
        state := true;
      end;
    end;
  end;

  if state then
    sl.Add(Copy(str,start,Length(str)));
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
  i: integer;
begin
  sl := TStringList.Create;
  try
    for i := 0 to Memo1.Lines.Count-1 do
    begin
      ExtractData(Memo1.Lines[i], sl);
      if sl.Count = 20 then
      begin
        StringGrid1.RowCount := StringGrid1.RowCount +1;
        StringGrid1.Rows[StringGrid1.RowCount-1].Assign(sl);
        sl.Clear;
      end;
    end;
  finally
    sl.Free;
  end;
end;

こんな感じでうまくいきました。

編集    削除
通りすがり  2005-07-08 00:18:54  No: 16187  IP: [192.*.*.*]

procedure TForm1.Button1Click(Sender: TObject);
const
  BYTES_PER_REC = 632;
var
  f: File;
  buf: array[1..BYTES_PER_REC] of Char;
  i: Integer;
begin
  AssignFile(f,'C:\Showcontig.txt');
  Reset(f,1);
  //
  i := 0;
  while not EOF(f) do begin
    BlockRead(f,buf,SizeOf(buf));
    StringGrid1.Rows[i].CommaText := buf;
    Inc(i);
  end;
  //
  CloseFile(f);
  //
  StringGrid1.RowCount := i;
end;
とか
procedure TForm1.Button2Click(Sender: TObject);
const
  LINES_PER_REC = 9;
var
  sl: TStringList;
  s: string;
  recCnt,i,j,k: Integer;
begin
  sl := TStringList.Create;
  sl.LoadFromFile('C:\Showcontig.txt');
  //
  recCnt := sl.Count div LINES_PER_REC;
  for i:=0 to recCnt-1 do begin
    s := '';
    k := i*LINES_PER_REC;
    for j:=k to k+(LINES_PER_REC-1) do
      s := s+sl[j];
    StringGrid1.Rows[i].CommaText := s;
  end;
  //
  sl.Free;
  //
  StringGrid1.RowCount := recCnt;
end;
では、ダメっすかね?

編集    削除
Streamを嫌うと今夜化けて出るぞ〜  2005-07-08 00:52:16  No: 16188  IP: [192.*.*.*]

>墓場のように避けている(^^)TFileStreamあたりを...

procedure TForm1.Button1Click(Sender: TObject);
var
  ms: TMemoryStream;
  pS, pD, pT: PChar;
  iCol, iRow: Integer;
  dCord: Boolean;
begin
  ms := TMemoryStream.Create;
  ms.LoadFromFile('__src.txt');   // 元のDATAファイル(20フィールド、複数レコード)
  pS := ms.Memory;
  pS[ms.Size] := #0;
  dCord := True;
  iCol := 0;
  iRow := 0;
  pD := pS;
  pT := pD;
  while pS^ <> #0 do begin
    if pS^ in [#9,#10,#13,#32] then begin
      if not dCord then begin
        pD^ := ',';
        inc(pD);
        inc(iCol);
      end;
      dCord := True;
    end else begin
      pD^ := pS^;
      inc(pD);
      dCord := False;
    end;
    inc(pS);
    // 1レコード分をStringGridにセット
    if iCol >= 20 then begin
      if (pD-1)^ = ',' then dec(pD);
      pD^ := #0;
      inc(iRow);
      StringGrid1.ColCount := 21;
      StringGrid1.RowCount := iRow + 1;
      StringGrid1.Rows[iRow].CommaText := 'レコード,'+ pT;
      pD^ := #13; inc(pD);
      pD^ := #10; inc(pD);
      pT := pD;
      iCol := 0;
    end;
  end;
  pD^ := #0;
  ms.Size := pD - ms.Memory;
  ms.SaveToFile('__dst.txt');     // CSV変換ファイル
  Memo1.SetTextBuf(ms.Memory);
  ms.Free;
end;

編集    削除
Zungige  2005-07-08 01:51:11  No: 16189  IP: [192.*.*.*]

count=18は行数を返していたのでした・・・(^^;)

anone様、通りすがり様数々の着眼点を簡明なコードで提示して頂きまして有難うございました。シックリと理解できました事をご報告いたします。
deldel様、3K様、ん?様適切な助言と味わい深いsuggestionを有難うございました。
Streamを嫌うと・・様、御教示くださいましたコードは諸般の事情(?)により、未だ理解の及ばない部分を含みますが、今後の課題を頂いたものと感謝いたします。
多くの方々の暖かいご指導に対して、深く御礼申し上げます。

編集    削除
deldel  2005-07-08 01:56:17  No: 16190  IP: [192.*.*.*]

1レコードが9行ならば、以下でもできました。

var
  sl1, sl2: TStringList;
  ib: Byte;
  sa: AnsiString;
  iwRowCount: Word;
begin
  sl1 := TStringList.Create;
  sl2 := TStringList.Create;
  try
    sl1.LoadFromFile('c:\showcontig.txt');

    iwRowCount := 0;
    while True do begin
      if sl1.Count < 9 then Break;

      sl2.Clear;
      for ib :=1 to 9 do begin
        sl2.Add(sl1[0]);
        sl1.Delete(0);
      end;

      sa := StringReplace(sl2.Text, #13#10, ',', [rfReplaceAll]);
      StringGrid1.Rows[iwRowCount].CommaText := sa;
      inc(iwRowCount);
    end;
  finally
    sl1.Free;
    sl2.Free;
  end;
end;

編集    削除
ん?  2005-07-08 02:59:16  No: 16191  IP: [192.*.*.*]

ちなみに、改行コードが入ってるだけで、実質固定長なんじゃ・・・?

編集    削除
んん  2005-07-08 04:24:39  No: 16192  IP: [192.*.*.*]

はじめから固定長ですけど

編集    削除
ん?  2005-07-08 10:12:46  No: 16193  IP: [192.*.*.*]

>はじめから固定長ですけど
どこに「固定長」と書いてあったんでしょう?

>のようにDBCC SHOWCONTIG  を -oスイッチをつけて実行して得たtxtファイルです。
もしかして、これが「固定長」と判断するもの?


ちなみに、固定長なら1レコード分読み込んで、CopyやTrim関数とか使って地道に切り崩すだけでよかったと思うのだが。。。
しかも「1レコードが9行」と決まっているような感じだし。

編集    削除