全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:4498
推到 Plurk!
推到 Facebook!

想讀取大型TXT檔案

答題得分者是:jow
rockhaword
一般會員


發表:3
回覆:5
積分:1
註冊:2011-05-30

發送簡訊給我
#1 引用回覆 回覆 發表時間:2011-06-23 15:16:28 IP:140.114.xxx.xxx 訂閱
我想設計一個程式可以讀取大型文字檔.txt(檔案大小1.3G)

檔案總共有90多萬筆資料 一行一筆 每一筆的長度不一
不知道有沒有方法可以抓取想要的第 i 行的資料就好

目前測試了StringList的方法 但是因為檔案過大 會發生Out of memory
[code delphi]
if (OpenDialog1->Execute()){

TStringList *SLTmp = new TStringList();
SLTmp->LoadFromFile(OpenDialog1->FileName);


for (int i=0; i < SLTmp->Count ; i )
{


Memo1->Lines->Add(SLTmp->Strings[i]);

}//end of while ( i < SLTmp->Count)
delete SLTmp;
}
[/code]
不一定要一次讀完整個檔案 請問有沒有方法可以只抓取想要的第 i 行的資料就好

編輯記錄
rockhaword 重新編輯於 2011-06-23 01:18:03, 註解 無‧
rockhaword 重新編輯於 2011-06-23 01:19:32, 註解 無‧
rockhaword 重新編輯於 2011-06-23 01:20:05, 註解 無‧
mephise
高階會員


發表:4
回覆:149
積分:205
註冊:2004-02-09

發送簡訊給我
#2 引用回覆 回覆 發表時間:2011-06-23 16:35:42 IP:220.137.xxx.xxx 訂閱
文字檔就是必須循序讀取, 如果可以直接跳到第N行的話, 那資料庫也沒啥用了

檔案太大可以試試看這個方式:

[code delphi]
var
f: TextFile
str: String;
begin
AssignFile(f, 'C:\data.txt');
Reset(f);

while not Eof(f) do begin
Readln(f, str);
// 把資料一次一行讀到 str, 然後再判斷 str 是不是你要的資料
end;
end;

[/code]

------
Mephise Chen
前興德工程師
ANDY8C
資深會員


發表:114
回覆:582
積分:299
註冊:2006-10-29

發送簡訊給我
#3 引用回覆 回覆 發表時間:2011-06-23 19:05:42 IP:210.66.xxx.xxx 訂閱
 

提醒您, ACCESS 不可行...


但我之前有將TXT 轉入 ACCESS (MDB) 中
方便 SQL 搜尋.....但是...大概只能轉入三萬多筆


------
---------------------------------------
偶爾才來 KTOP ,交流條碼問題,在 FB [條碼標籤達人] 社團留言,感恩.
rockhaword
一般會員


發表:3
回覆:5
積分:1
註冊:2011-05-30

發送簡訊給我
#4 引用回覆 回覆 發表時間:2011-06-23 21:10:32 IP:140.114.xxx.xxx 訂閱
感謝回覆
用的大大的方法試過了
讀取小型檔案OK沒問題

但是讀取1.3G的大檔案就完全沒反應
還是感謝大大的回覆

===================引 用 mephise 文 章===================
文字檔就是必須循序讀取, 如果可以直接跳到第N行的話, 那資料庫也沒啥用了

檔案太大可以試試看這個方式:

[code delphi]
var
f: TextFile
str: String;
begin
AssignFile(f, 'C:\data.txt');
Reset(f);

while not Eof(f) do begin
Readln(f, str);
// 把資料一次一行讀到 str, 然後再判斷 str 是不是你要的資料
end;
end;

[/code]

mephise
高階會員


發表:4
回覆:149
積分:205
註冊:2004-02-09

發送簡訊給我
#5 引用回覆 回覆 發表時間:2011-06-23 21:51:27 IP:220.137.xxx.xxx 訂閱
沒反應不代表失敗喔, 可能只是系統I/O一直在忙碌而以
Windows 系統有個通病, 當它正在忙綠時, 你如果點一下視窗, 它就會顯示
"這個程式已經沒有回應 bala bala ........."
你可以打開程式管理員, 看看你的程式的CPU使用率是不是達到了100%
如果是, 而你又不是無限回圈, 那就再等等吧!
1.3G的檔案光是Copy也要不少時間咧! 更何況是還要運算 ^^

===================引 用 rockhaword 文 章===================
感謝回覆
用的大大的方法試過了
讀取小型檔案OK沒問題

但是讀取1.3G的大檔案就完全沒反應
還是感謝大大的回覆

===================引 用 mephise 文 章===================
文字檔就是必須循序讀取, 如果可以直接跳到第N行的話, 那資料庫也沒啥用了

檔案太大可以試試看這個方式:

[code delphi]
var
f: TextFile
str: String;
begin
AssignFile(f, 'C:\data.txt');
Reset(f);

while not Eof(f) do begin
Readln(f, str);
// 把資料一次一行讀到 str, 然後再判斷 str 是不是你要的資料
end;
end;

[/code]

------
Mephise Chen
前興德工程師
GrandRURU
站務副站長


發表:234
回覆:1651
積分:1742
註冊:2005-06-21

發送簡訊給我
#6 引用回覆 回覆 發表時間:2011-06-24 15:15:02 IP:219.87.xxx.xxx 未訂閱
你可以利用TMemorystream來做分批讀取
可參考:http://www.delphigroups.info/3/8/442255.html
擷取一小部份程式碼
[code cpp]
// Untested.
TFileStream *FileStream;
TMemoryStream *MemoryStream;
FileStream = new TFileStream( "file.txt", fmOpenRead |
fmShareDenyWrite );
MemoryStream = new TMemoryStream;
MemoryStream->CopyFrom( FileStream, BytesToRead );
// Now use the contents of the memory stream and keep
// doing CopyFrom to get new chunks until it returns 0,
// for no bytes left.
delete MemoryStream;
delete FileStream;
[/code]


===================引 用 rockhaword 文 章===================
我想設計一個程式可以讀取大型文字檔.txt(檔案大小1.3G)

檔案總共有90多萬筆資料 一行一筆 每一筆的長度不一
不知道有沒有方法可以抓取想要的第 i 行的資料就好

目前測試了StringList的方法 但是因為檔案過大 會發生Out of memory
[code delphi]
if (OpenDialog1->Execute()){

TStringList *SLTmp = new TStringList();
SLTmp->LoadFromFile(OpenDialog1->FileName);


for (int i=0; i < SLTmp->Count ; i )
{


Memo1->Lines->Add(SLTmp->Strings[i]);

}//end of while ( i < SLTmp->Count)
delete SLTmp;
}
[/code]
不一定要一次讀完整個檔案 請問有沒有方法可以只抓取想要的第 i 行的資料就好


編輯記錄
GrandRURU 重新編輯於 2011-06-24 01:16:35, 註解 無‧
jow
尊榮會員


發表:66
回覆:751
積分:1253
註冊:2002-03-13

發送簡訊給我
#7 引用回覆 回覆 發表時間:2011-06-24 23:57:15 IP:123.193.xxx.xxx 未訂閱
以Delphi提供個人做法,謹供參考

1.產生測試用的 DATA.TXT
[code delphi]
procedure TForm1.Button3Click(Sender: TObject);
const
FN_DATA='\DATA.TXT';
FM_PROGRESS='Count=%d, Progress=%.2f%%';
FM_FILEINFO='Count=%d, FileSize=%.0f';
var
S: string;
Count,J,K: Integer;
DATA: TFileStream;
BytesWritten,MAX_FILE_SIZE: Double;

procedure ShowProgress;
begin
Label1.Caption := Format(FM_PROGRESS,[Count,BytesWritten*100/MAX_FILE_SIZE]);
Label1.Update;
end;

begin
MAX_FILE_SIZE := 1024*1024*10;//*1024*1024*1024*1.5;
if not FileExists(FN_DATA) then
begin
DATA := TFileStream.Create(FN_DATA,fmCreate);
try
Count := 0;
BytesWritten := 0;
repeat
K := Max(1,Random(50));
S := IntToStr(Count);//The No.
for J := 0 to K do
S := Format('%s,%d',[S,Random( MaxInt)]);
S := S #$D #$A;
BytesWritten := BytesWritten DATA.Write(PChar(S)^,Length(S));
Inc(Count);
if Count mod 100 = 0 then ShowProgress;
until BytesWritten>MAX_FILE_SIZE;
ShowProgress;
ShowMessage(Format(FM_FILEINFO,[Count,BytesWritten]));
finally
FreeAndNil(DATA);
end;
end;
end;
[/code]

2.建立索引檔 INDEX.DAT
[code delphi]
procedure TForm1.Button4Click(Sender: TObject);
const
FN_INDEX='\INDEX.DAT';
FN_DATA='\DATA.TXT';
FM_PROGRESS='Count=%d, Progress=%.2f%%';
type
TIndexRec = packed record
No: Integer;
Pos: Int64;
Len: Integer;
end;
var
C: Char;
I,St,BytesRead: Integer;
L: TStringList;
INDEX,DATA: TFileStream;
r: TIndexRec;
B: array[0..1024*64-1] of Char;
begin
if not FileExists(FN_INDEX) and FileExists(FN_DATA) then
begin
INDEX := TFileStream.Create(FN_INDEX,fmCreate);
DATA := TFileStream.Create(FN_DATA,fmOpenRead);
try
C := #$0;
FillChar(r,SizeOf(r),#$0);
while DATA.Position begin
St := DATA.Position;
BytesRead := DATA.Read(B,SizeOf(B));
for I := 0 to BytesRead-1 do
begin
if (C=#$D) and (B[I]=#$A) then
begin
C := #$0;
r.Len := St I-r.Pos 1;
INDEX.Write(r,SizeOf(r));
Inc(r.No);
r.Pos := St I 1;
r.Len := 0;
Continue;
end;

if (C=#$0) and (B[I]=#$D) then C := #$D;

end;
Label2.Caption := Format(FM_PROGRESS,[r.No,(DATA.Position 1)*100/DATA.Size]);
Label2.Update;
end;
finally
FreeAndNil(DATA);
FreeAndNil(INDEX);
end;
end;

//TestDump: INDEX.DAT
if FileExists(FN_INDEX) then
begin
INDEX := TFileStream.Create(FN_INDEX,fmOpenRead);
try
INDEX.Position := 0;
L := TStringList.Create;
try
while INDEX.Position if INDEX.Read(r,SizeOf(r))=SizeOf(r) then
L.Add(Format('%d, %d, %d',[r.No,r.Pos,r.Len]));
ListBox1.Items.Text := L.Text;
finally
FreeAndNil(L);
end;
finally
FreeAndNil(INDEX);
end;
end;

end;
[/code]

3. 利用 INDEX.DAT 對 DATA.TXT 做隨機讀取測試
[code delphi]
procedure TForm1.Button5Click(Sender: TObject);
const
FN_INDEX='\INDEX.DAT';
FN_DATA='\DATA.TXT';
FM_OUTPUT='N=%d->No=%d->%s';
type
TIndexRec = packed record
No: Integer;
Pos: Int64;
Len: Integer;
end;
var
S: string;
I,N,Count: Integer;
L: TStringList;
INDEX,DATA: TFileStream;
r: TIndexRec;
begin
if FileExists(FN_INDEX) and FileExists(FN_DATA) then
begin
INDEX := TFileStream.Create(FN_INDEX,fmOpenRead);
DATA := TFileStream.Create(FN_DATA,fmOpenRead);
L := TStringList.Create;
try
Count := INDEX.Size div SizeOf(TIndexRec);
for I := 0 to 999 do
begin
N := Random(Count);
INDEX.Position := SizeOf(TIndexRec)*N;
if INDEX.Read(r,SizeOf(r))=SizeOf(r) then
begin
SetLength(S,r.Len);
try
DATA.Position := r.Pos;
if DATA.Read(PChar(S)^,r.Len)=r.Len then
begin
L.Add(Format(FM_OUTPUT,[N,r.No,S]));
end;
finally
S := '';
end;
end;
end;
ListBox1.Items.Text := L.Text;
finally
FreeAndNil(L);
FreeAndNil(DATA);
FreeAndNil(INDEX);
end;
end;
end;
[/code]

4.刪除測試檔案 INDEX.DAT 及 DATA.TXT
[code delphi]
procedure TForm1.Button6Click(Sender: TObject);
const
FN_INDEX='\INDEX.DAT';
FN_DATA='\DATA.TXT';
begin
if FileExists(FN_INDEX) then DeleteFile(FN_INDEX);
if FileExists(FN_DATA) then DeleteFile(FN_DATA);
end;
[/code]



rockhaword
一般會員


發表:3
回覆:5
積分:1
註冊:2011-05-30

發送簡訊給我
#8 引用回覆 回覆 發表時間:2011-06-25 16:37:12 IP:140.114.xxx.xxx 訂閱
感謝jow大大的回覆
我用了大大的方法 終於成功讀取到這筆資料
真的很感動
非常感謝!!
rockhaword
一般會員


發表:3
回覆:5
積分:1
註冊:2011-05-30

發送簡訊給我
#9 引用回覆 回覆 發表時間:2011-07-14 23:59:12 IP:140.114.xxx.xxx 訂閱


你好
不好意思再打擾您

想問你一下
我用你的方法成功的讀取到大型文字檔
但有一個問題
如果短時間連續讀取會一直吃記憶體
最後造成Out of Memory

我用迴圈一直重複執行以下code
[code delphi]
N := i;
INDEX.Position := SizeOf(TIndexRec)*N;
if INDEX.Read(r,SizeOf(r))=SizeOf(r) then
begin
SetLength(S,r.Len);
DATA.Position := r.Pos;
if DATA.Read(PChar(S)^,r.Len)=r.Len then
begin
L.Add(Format(FM_OUTPUT,[N,r.No,S]));
end;
end;
[/code]
結果會記憶體使用量會急速上升
估計是S這個string吃掉記憶體
但是不知道該怎麼手動釋放
請問有辦法解決這個問題嗎
再次謝謝你喔
===================引 用 jow 文 章===================
以Delphi提供個人做法,謹供參考

1.產生測試用的 DATA.TXT
[code delphi]
procedure TForm1.Button3Click(Sender: TObject);
const
FN_DATA='\DATA.TXT';
FM_PROGRESS='Count=%d, Progress=%.2f%%';
FM_FILEINFO='Count=%d, FileSize=%.0f';
var
S: string;
Count,J,K: Integer;
DATA: TFileStream;
BytesWritten,MAX_FILE_SIZE: Double;

procedure ShowProgress;
begin
Label1.Caption := Format(FM_PROGRESS,[Count,BytesWritten*100/MAX_FILE_SIZE]);
Label1.Update;
end;

begin
MAX_FILE_SIZE := 1024*1024*10;//*1024*1024*1024*1.5;
if not FileExists(FN_DATA) then
begin
DATA := TFileStream.Create(FN_DATA,fmCreate);
try
Count := 0;
BytesWritten := 0;
repeat
K := Max(1,Random(50));
S := IntToStr(Count);//The No.
for J := 0 to K do
S := Format('%s,%d',[S,Random( MaxInt)]);
S := S #$D #$A;
BytesWritten := BytesWritten DATA.Write(PChar(S)^,Length(S));
Inc(Count);
if Count mod 100 = 0 then ShowProgress;
until BytesWritten>MAX_FILE_SIZE;
ShowProgress;
ShowMessage(Format(FM_FILEINFO,[Count,BytesWritten]));
finally
FreeAndNil(DATA);
end;
end;
end;
[/code]

2.建立索引檔 INDEX.DAT
[code delphi]
procedure TForm1.Button4Click(Sender: TObject);
const
FN_INDEX='\INDEX.DAT';
FN_DATA='\DATA.TXT';
FM_PROGRESS='Count=%d, Progress=%.2f%%';
type
TIndexRec = packed record
No: Integer;
Pos: Int64;
Len: Integer;
end;
var
C: Char;
I,St,BytesRead: Integer;
L: TStringList;
INDEX,DATA: TFileStream;
r: TIndexRec;
B: array[0..1024*64-1] of Char;
begin
if not FileExists(FN_INDEX) and FileExists(FN_DATA) then
begin
INDEX := TFileStream.Create(FN_INDEX,fmCreate);
DATA := TFileStream.Create(FN_DATA,fmOpenRead);
try
C := #$0;
FillChar(r,SizeOf(r),#$0);
while DATA.Position begin
St := DATA.Position;
BytesRead := DATA.Read(B,SizeOf(B));
for I := 0 to BytesRead-1 do
begin
if (C=#$D) and (B[I]=#$A) then
begin
C := #$0;
r.Len := St I-r.Pos 1;
INDEX.Write(r,SizeOf(r));
Inc(r.No);
r.Pos := St I 1;
r.Len := 0;
Continue;
end;

if (C=#$0) and (B[I]=#$D) then C := #$D;

end;
Label2.Caption := Format(FM_PROGRESS,[r.No,(DATA.Position 1)*100/DATA.Size]);
Label2.Update;
end;
finally
FreeAndNil(DATA);
FreeAndNil(INDEX);
end;
end;

//TestDump: INDEX.DAT
if FileExists(FN_INDEX) then
begin
INDEX := TFileStream.Create(FN_INDEX,fmOpenRead);
try
INDEX.Position := 0;
L := TStringList.Create;
try
while INDEX.Position if INDEX.Read(r,SizeOf(r))=SizeOf(r) then
L.Add(Format('%d, %d, %d',[r.No,r.Pos,r.Len]));
ListBox1.Items.Text := L.Text;
finally
FreeAndNil(L);
end;
finally
FreeAndNil(INDEX);
end;
end;

end;
[/code]

3. 利用 INDEX.DAT 對 DATA.TXT 做隨機讀取測試
[code delphi]
procedure TForm1.Button5Click(Sender: TObject);
const
FN_INDEX='\INDEX.DAT';
FN_DATA='\DATA.TXT';
FM_OUTPUT='N=%d->No=%d->%s';
type
TIndexRec = packed record
No: Integer;
Pos: Int64;
Len: Integer;
end;
var
S: string;
I,N,Count: Integer;
L: TStringList;
INDEX,DATA: TFileStream;
r: TIndexRec;
begin
if FileExists(FN_INDEX) and FileExists(FN_DATA) then
begin
INDEX := TFileStream.Create(FN_INDEX,fmOpenRead);
DATA := TFileStream.Create(FN_DATA,fmOpenRead);
L := TStringList.Create;
try
Count := INDEX.Size div SizeOf(TIndexRec);
for I := 0 to 999 do
begin
N := Random(Count);
INDEX.Position := SizeOf(TIndexRec)*N;
if INDEX.Read(r,SizeOf(r))=SizeOf(r) then
begin
SetLength(S,r.Len);
try
DATA.Position := r.Pos;
if DATA.Read(PChar(S)^,r.Len)=r.Len then
begin
L.Add(Format(FM_OUTPUT,[N,r.No,S]));
end;
finally
S := '';
end;
end;
end;
ListBox1.Items.Text := L.Text;
finally
FreeAndNil(L);
FreeAndNil(DATA);
FreeAndNil(INDEX);
end;
end;
end;
[/code]

4.刪除測試檔案 INDEX.DAT 及 DATA.TXT
[code delphi]
procedure TForm1.Button6Click(Sender: TObject);
const
FN_INDEX='\INDEX.DAT';
FN_DATA='\DATA.TXT';
begin
if FileExists(FN_INDEX) then DeleteFile(FN_INDEX);
if FileExists(FN_DATA) then DeleteFile(FN_DATA);
end;
[/code]



jow
尊榮會員


發表:66
回覆:751
積分:1253
註冊:2002-03-13

發送簡訊給我
#10 引用回覆 回覆 發表時間:2011-07-15 11:51:24 IP:112.104.xxx.xxx 未訂閱
將下列程式Mark起來,
應該是ListBox被塞爆了... ^_^

[code delphi]
//TestDump: INDEX.DAT
if FileExists(FN_INDEX) then
begin
INDEX := TFileStream.Create(FN_INDEX,fmOpenRead);
try
INDEX.Position := 0;
L := TStringList.Create;
try
while INDEX.Position if INDEX.Read(r,SizeOf(r))=SizeOf(r) then
L.Add(Format('%d, %d, %d',[r.No,r.Pos,r.Len]));
ListBox1.Items.Text := L.Text;
finally
FreeAndNil(L);
end;
finally
FreeAndNil(INDEX);
end;
end;
[/code]
rockhaword
一般會員


發表:3
回覆:5
積分:1
註冊:2011-05-30

發送簡訊給我
#11 引用回覆 回覆 發表時間:2011-07-15 14:42:24 IP:140.114.xxx.xxx 訂閱
感恩
問題解決了
原來是StringList L佔用了記憶體空間
真的非常感謝!!!!
系統時間:2017-10-24 17:40:03
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!