請教"大量文字資料檔,以TStringList載入"問題 |
答題得分者是:shunaaron
|
ac910127
一般會員 發表:7 回覆:27 積分:11 註冊:2009-11-06 發送簡訊給我 |
各位前輩 您們好
小弟在此有一問題,請前輩們不吝惜給我建議與指教。 目前有一ID對照表(XXXX.txt),內容為多筆身分證字號(大概二百萬筆左右),大小約100多MB, 由於程式執行過程,需要此表對照。 第一時間小弟想到以"TStringList"方式載入,利用TStringList特性方便找尋KEY值。但問題來了 1 : TStringList能負載的大小為多少,以小弟目前的認知,一百多MB是OK的,實不知道真實上限為多少? 由於ID對照表可能隨時間增長,怕無法負載!! 2 : 在找尋TStringList內容值時,小弟利用 tIntIndex:=TStringList.IndexOf('S123456789');來取得位子,可是不知是因為太大量還是怎樣? 回傳的 tIntIndex:=-1; << 表示沒找到。小弟可確認"S123456789"確實存在,不過位子在一百八十幾萬的位子。 因此小弟改以利用迴圈方式找尋 for i=0 to TStringList.Count-1 do begin ...... ...... end; 這樣的話就可正確找到資料。不知前輩能否指導兩種方式為啥出現不同的結果,差別在哪 ??? 3 : 由於ID對照表可能會隨著時間而量不斷增加,深怕"TStringList"日後無法負載如此大量的資料,尋找速度變慢之類的。 小弟目前只想到以"TStringList"來處理,不知前輩可否其他建議 ? 以上,先感謝前輩們的指導!! 編輯記錄
ac910127 重新編輯於 2010-11-30 19:33:51, 註解 無‧
|
pprayer
高階會員 發表:35 回覆:185 積分:174 註冊:2002-03-13 發送簡訊給我 |
|
ac910127
一般會員 發表:7 回覆:27 積分:11 註冊:2009-11-06 發送簡訊給我 |
|
shunaaron
高階會員 發表:13 回覆:94 積分:106 註冊:2006-10-06 發送簡訊給我 |
tIntIndex:=TStringList.IndexOf('S123456789');
IndexO最後是用PChar來比對, 看一下文字檔格式內碼為何 ===================引 用 ac910127 文 章=================== 各位前輩 您們好 小弟在此有一問題,請前輩們不吝惜給我建議與指教。 目前有一ID對照表(XXXX.txt),內容為多筆身分證字號(大概二百萬筆左右),大小約100多MB, 由於程式執行過程,需要此表對照。 第一時間小弟想到以"TStringList"方式載入,利用TStringList特性方便找尋KEY值。但問題來了 1 : TStringList能負載的大小為多少,以小弟目前的認知,一百多MB是OK的,實不知道真實上限為多少? 由於ID對照表可能隨時間增長,怕無法負載!! 2 : 在找尋TStringList內容值時,小弟利用 tIntIndex:=TStringList.IndexOf('S123456789');來取得位子,可是不知是因為太大量還是怎樣? 回傳的 tIntIndex:=-1; << 表示沒找到。小弟可確認"S123456789"確實存在,不過位子在一百八十幾萬的位子。 因此小弟改以利用迴圈方式找尋 for i=0 to TStringList.Count-1 do begin ...... ...... end; 這樣的話就可正確找到資料。不知前輩能否指導兩種方式為啥出現不同的結果,差別在哪 ??? 3: 由於ID對照表可能會隨著時間而量不斷增加,深怕"TStringList"日後無法負載如此大量的資料,尋找速度變慢之類的。 小弟目前只想到以"TStringList"來處理,不知前輩可否其他建議 ? 以上,先感謝前輩們的指導!!
------
程式沒有這麼難 只是還沒打通其中要絕 |
max5020
資深會員 發表:30 回覆:277 積分:321 註冊:2003-06-04 發送簡訊給我 |
|
ac910127
一般會員 發表:7 回覆:27 積分:11 註冊:2009-11-06 發送簡訊給我 |
|
ac910127
一般會員 發表:7 回覆:27 積分:11 註冊:2009-11-06 發送簡訊給我 |
|
shunaaron
高階會員 發表:13 回覆:94 積分:106 註冊:2006-10-06 發送簡訊給我 |
如果文檔確認是unicode的話,那先除去編碼問題
可試下TStringList.find('你要找的字串',第幾個指標開始找) 假設有100萬筆資料,分100次去做比對,試看看吧。 第一次給0....之後給10001....20001 可以自己在調看看可以到多少 不過一次應極限應在3萬行...(2^16)猜測 ===================引 用 ac910127 文 章=================== 感謝大大的回應!! 文字檔內容包含"一些特殊字元",故格式內碼應該為"Unicode"編碼方式存儲 另外小弟使用的Delphi版本為 Delphi2010 請教大大這是否有影響 ? 再次感謝大大的回應!! ===================引 用 shunaaron 文 章=================== tIntIndex:=TStringList.IndexOf('S123456789'); IndexO最後是用PChar來比對, 看一下文字檔格式內碼為何
------
程式沒有這麼難 只是還沒打通其中要絕 |
ac910127
一般會員 發表:7 回覆:27 積分:11 註冊:2009-11-06 發送簡訊給我 |
感謝大大再次的回覆,並提供建議。
由於不太清楚TStringList.Find 的用法,故在看了下HELP,並根據HELP提供的範例 與大大的建議,寫兩段Code !! Code 1 - 根據HELP的範例 [code delphi] procedure TForm1.Button1Click(Sender: TObject); var IDList : TStringList; tIntFund : Integer; tA: Extended; //紀錄處理時間 begin IDList:=TStringList.Create; tA:=Now; IDList.LoadFromFile('C:\123456.txt'); IDList.Sort; //根據 HELP說明 需要sort if IDList.Find('0000003BCA',tIntFund) then begin //在1822378的位置 Memo1.Lines.Add(IntToStr(tIntFund)); //有發現時 顯示所在位置 Memo1.Lines.Add(IDList.Strings[tIntFund]); //有發現時 顯示內容 Memo1.Lines.Add(FormatDateTime('HH:NN:SS:ZZZ',Now-tA)); //顯示處理時間 end; IDList.Free; end; [/code] Code 2 - 根據大大的建議 [code delphi] procedure TForm1.Button2Click(Sender: TObject); var IDList : TStringList; tIntFund : Integer; tA: Extended; begin IDList:=TStringList.Create; tA:=Now; IDList.LoadFromFile('C:\123456.txt'); IDList.Sort; tIntFund:=0; while tIntFund > IDList.Count do begin if IDList.Find('0000003BCA',tIntFund) then begin //在1822378的位置 Memo1.Lines.Add(IDList.Strings[tIntFund]); //有發現時 顯示內容 Memo1.Lines.Add(FormatDateTime('HH:NN:SS:ZZZ',Now-tA)); //顯示處理時間 break; end else begin Inc(tIntFund,30000); //沒有發現時,起始位置加三萬 end; end; IDList.Free; end; [/code] 以上兩種方式均無法找到。不知是小弟哪邊錯誤了!!! 另外,TStrginList.Find 是否是從Index的位置開始搜尋 到 最尾端?? 還是 ?? 煩請大大再次為小弟解惑 !! 再次感謝大大的回覆 !! PS:本來想上傳ID對照檔,可是小弟好像沒辦法上傳檔案了,以下提供ID對照表格式 身分證字號 會員編號 性別 出生年月日 姓名 F234567890 000000002C M 130425 馬00 Y123456789 000000001B M 140316 羅00 F234567890 000000002C M 130425 馬00 ..... ..... ..... U122778899 0000000001 F 121212 堃一芬 S123119999 000000100A M 841010 犇煚囧 E122000111 0000010A0B F 730123 燚燚缕 F123119988 0000003BCA M 841010 囧rz犇 G122113355 0000041GHA M 841010 囧rz鱻靎 ===================引 用 shunaaron 文 章=================== 如果文檔確認是unicode的話,那先除去編碼問題 可試下TStringList.find('你要找的字串',第幾個指標開始找) 假設有100萬筆資料,分100次去做比對,試看看吧。 第一次給0....之後給10001....20001 可以自己在調看看可以到多少 不過一次應極限應在3萬行...(2^16)猜測 ===================引 用 ac910127 文 章=================== 感謝大大的回應!! 文字檔內容包含"一些特殊字元",故格式內碼應該為"Unicode"編碼方式存儲 另外小弟使用的Delphi版本為 Delphi2010 請教大大這是否有影響 ? 再次感謝大大的回應!! ===================引 用 shunaaron 文 章=================== tIntIndex:=TStringList.IndexOf('S123456789'); IndexO最後是用PChar來比對, 看一下文字檔格式內碼為何 |
shunaaron
高階會員 發表:13 回覆:94 積分:106 註冊:2006-10-06 發送簡訊給我 |
剛有測試過到10萬多筆還是可以找到,
所以要確認一件事情,當把文字檔讀入TStringList裡時,是將所有資料都讀進去 如:身份字號 姓名等都讀入TStringList裡 如果是這樣的話IndexOf一定會找不到, 因為IndexOf是用完全比對,也就是說你放到TStringList的資料只能是身份字號 不能包含其他資料 另外Find是從你給的指標位子往下開始找到未筆, 剛又測到20萬都可以找到,問題可能出在你讀進來資料, 是只有身份字號或全部資料了 ===================引 用 ac910127 文 章=================== 感謝大大再次的回覆,並提供建議。 由於不太清楚TStringList.Find 的用法,故在看了下HELP,並根據HELP提供的範例 與大大的建議,寫兩段Code !! Code 1 - 根據HELP的範例 [code delphi] procedure TForm1.Button1Click(Sender: TObject); var IDList : TStringList; tIntFund : Integer; tA: Extended; //紀錄處理時間 begin IDList:=TStringList.Create; tA:=Now; IDList.LoadFromFile('C:\123456.txt'); IDList.Sort; //根據 HELP說明 需要sort if IDList.Find('0000003BCA',tIntFund) then begin //在1822378的位置 Memo1.Lines.Add(IntToStr(tIntFund)); //有發現時 顯示所在位置 Memo1.Lines.Add(IDList.Strings[tIntFund]); //有發現時 顯示內容 Memo1.Lines.Add(FormatDateTime('HH:NN:SS:ZZZ',Now-tA)); //顯示處理時間 end; IDList.Free; end; [/code] Code 2 - 根據大大的建議 [code delphi] procedure TForm1.Button2Click(Sender: TObject); var IDList : TStringList; tIntFund : Integer; tA: Extended; begin IDList:=TStringList.Create; tA:=Now; IDList.LoadFromFile('C:\123456.txt'); IDList.Sort; tIntFund:=0; while tIntFund > IDList.Count do begin if IDList.Find('0000003BCA',tIntFund) then begin //在1822378的位置 Memo1.Lines.Add(IDList.Strings[tIntFund]); //有發現時 顯示內容 Memo1.Lines.Add(FormatDateTime('HH:NN:SS:ZZZ',Now-tA)); //顯示處理時間 break; end else begin Inc(tIntFund,30000); //沒有發現時,起始位置加三萬 end; end; IDList.Free; end; [/code] 以上兩種方式均無法找到。不知是小弟哪邊錯誤了!!! 另外,TStrginList.Find 是否是從Index的位置開始搜尋 到 最尾端?? 還是 ?? 煩請大大再次為小弟解惑 !! 再次感謝大大的回覆 !! PS:本來想上傳ID對照檔,可是小弟好像沒辦法上傳檔案了,以下提供ID對照表格式 身分證字號 會員編號 性別 出生年月日 姓名 F234567890 000000002C M 130425 馬00 Y123456789 000000001B M 140316 羅00 F234567890 000000002C M 130425 馬00 ..... ..... ..... U122778899 0000000001 F 121212 堃一芬 S123119999 000000100A M 841010 犇煚囧 E122000111 0000010A0B F 730123 燚燚缕 F123119988 0000003BCA M 841010 囧rz犇 G122113355 0000041GHA M 841010 囧rz鱻靎 ===================引 用 shunaaron 文 章=================== 如果文檔確認是unicode的話,那先除去編碼問題 可試下TStringList.find('你要找的字串',第幾個指標開始找) 假設有100萬筆資料,分100次去做比對,試看看吧。 第一次給0....之後給10001....20001 可以自己在調看看可以到多少 不過一次應極限應在3萬行...(2^16)猜測 ===================引 用 ac910127 文 章=================== 感謝大大的回應!! 文字檔內容包含"一些特殊字元",故格式內碼應該為"Unicode"編碼方式存儲 另外小弟使用的Delphi版本為 Delphi2010 請教大大這是否有影響 ? 再次感謝大大的回應!! ===================引 用 shunaaron 文 章=================== tIntIndex:=TStringList.IndexOf('S123456789'); IndexO最後是用PChar來比對, 看一下文字檔格式內碼為何
------
程式沒有這麼難 只是還沒打通其中要絕
編輯記錄
shunaaron 重新編輯於 2010-11-30 20:55:24, 註解 無‧
|
mephise
高階會員 發表:4 回覆:149 積分:205 註冊:2004-02-09 發送簡訊給我 |
您的資料格式是這樣
ex: U122778899 0000000001 F 121212 堃一芬 所以 TStringList 讀取的時候是以"行"為單位的 也就是說 fStringList.IndexOf('U122778899 0000000001 F 121212 堃一芬'); 這樣才會找得到 fStringList.IndexOf('U122778899'); 這樣就會找不到 所以在資料讀取上應該還要做一些手腳, 把欄位分開 所以找不到跟編碼無關 因為你資料有堃這個字, 所以它是 UTF-8 而不是 ANSI, 您用的是2010內定就是 UTF-8, 當然更沒問題了 此外TStringList的上限應該跟String一樣,2G!
------
Mephise Chen 前興德工程師
編輯記錄
mephise 重新編輯於 2010-11-30 22:32:25, 註解 無‧
|
ac910127
一般會員 發表:7 回覆:27 積分:11 註冊:2009-11-06 發送簡訊給我 |
感謝大大再次回答並測試 !!
讀進來的資料為全部資料。 大大測試筆數為十萬~二十萬筆,如果大大有空,可否測試資料一百五十萬筆以上 ?? 再者,大大說"Find是從你給的指標位子往下開始找到未筆" 這樣是否代表著一定要 從第零筆開始搜尋,因為無法確認"要搜尋的資料在哪個區段",這樣是否跟大大一 開始建議的方式有出入!!! 還是我誤解大大的意思,請大大解惑。 再次感謝大大的回覆!! ===================引 用 shunaaron 文 章=================== 剛有測試過到10萬多筆還是可以找到, 所以要確認一件事情,當把文字檔讀入TStringList裡時,是將所有資料都讀進去 如:身份字號 姓名等都讀入TStringList裡 如果是這樣的話IndexOf一定會找不到, 因為IndexOf是用完全比對,也就是說你放到TStringList的資料只能是身份字號 不能包含其他資料 另外Find是從你給的指標位子往下開始找到未筆, 剛又測到20萬都可以找到,問題可能出在你讀進來資料, 是只有身份字號或全部資料了 |
ac910127
一般會員 發表:7 回覆:27 積分:11 註冊:2009-11-06 發送簡訊給我 |
感謝大大的回覆!! 大大精闢的解答,讓小弟更加了解TStringList.IndexOf運用。 不過目前正再嘗試 shunaaron 前輩提供"TStringList.Find"進行測試, 由於小弟的資料是百萬筆,導致還是未能找到正確資料。 依舊嘗試中 !! 再次感謝大大的解答。 ===================引 用 mephise 文 章=================== 您的資料格式是這樣 ex: U122778899 0000000001 F 121212 堃一芬 所以 TStringList 讀取的時候是以"行"為單位的 也就是說 fStringList.IndexOf('U122778899 0000000001 F 121212 堃一芬'); 這樣才會找得到 fStringList.IndexOf('U122778899'); 這樣就會找不到 所以在資料讀取上應該還要做一些手腳, 把欄位分開 所以找不到跟編碼無關 因為你資料有堃這個字, 所以它是 UTF-8 而不是 ANSI, 您用的是2010內定就是 UTF-8, 當然更沒問題了 此外TStringList的上限應該跟String一樣,2G!
編輯記錄
ac910127 重新編輯於 2010-12-01 00:14:47, 註解 無‧
|
shunaaron
高階會員 發表:13 回覆:94 積分:106 註冊:2006-10-06 發送簡訊給我 |
看一下紅色字的部份,應是你錯誤的部份了,在看上面ephise
有更清的說明 之前以為TStringList有他的找資料列的大小, 並沒有在往下找就停止比對, 才見意你改成分段模式, 如果是有2G的空間的話, 可以不用使用Find, 直接使用indexof即可 ===================引 用 ac910127 文 章=================== 感謝大大再次回答並測試 !! 如:身份字號 姓名等都讀入TStringList裡 如果是這樣的話IndexOf一定會找不到, 因為IndexOf是用完全比對,也就是說你放到TStringList的資料只能是身份字號 不能包含其他資料 另外Find是從你給的指標位子往下開始找到未筆, 剛又測到20萬都可以找到,問題可能出在你讀進來資料, 是只有身份字號或全部資料了
------
程式沒有這麼難 只是還沒打通其中要絕 |
ac910127
一般會員 發表:7 回覆:27 積分:11 註冊:2009-11-06 發送簡訊給我 |
感謝各位前輩熱心的指導 !!
pprayer前輩 提供以TFileStream方式處理,小弟測試過後,發現 會因長度不固定,而取得非小弟想像中的字串內容,以小弟目前處理上來說, 會有錯誤,故暫不考慮。 感謝shunaaron 前輩 與 mephise前輩再 TStringList上觀念指導, 由於 shunaaron 前輩先指出錯誤點在哪,故此給分給予shunaaron 前輩。 再次感謝前輩們的指導。
編輯記錄
ac910127 重新編輯於 2010-12-01 00:32:09, 註解 無‧
|
Victor4022
中階會員 發表:0 回覆:76 積分:90 註冊:2011-02-20 發送簡訊給我 |
您好, 有點晚看到此題目, 不過以下是我的想法:
1. 善用TStringList.Find function, 因為binary search 很有效率, 前提是您的資料必須是要已經排序完畢. 使用IndexOf的話如果資料在第100萬筆時, for loop就必須跑到第100萬次才相符, 如果資料已經先排序後, 使用TStringlist.Find 最\佳情形僅須要1次的代價. 如何實作排序? [code delphi] TStringList.LoadFormFile(your file name); TStringList.Sort; TStringList.SaveToFile(your sorted file name); [/code] 這樣下一次使用Find就很快 2. 因為您的資料是由多個欄位組成一行字串而成, 而不管使用IndexOf 或是 Find都是"字串完整"比對, 因此建議您將資料進行欄位切割, 實作2個自訂的object就能達到管理這份200mb的檔案(不過欄位內容請依您的需求進行調整, 以下只是示意程式, 不保證沒bug :P), 例如以下程式: 我用TMyData當成您每一行內的獨立資料, TMyList則容納了您所有的獨立資料, 可以把TMyData想像成一個字串parser, 而程式利用身粉證字號當成TStringList的primary key來使用, 其餘data則利用TMyData這個TObject的其他欄位記下. [code delphi] type TForm1 = class(TForm) btnTest: TButton; procedure btnTestClick(Sender: TObject); private { Private declarations } public { Public declarations } end; type TMyData = class(TObject) private FSn, FName, FAddr : String; public property Sn : String read FSn; property Name : String read FName; property Addr : String read FAddr; constructor Create(const RowData : String); end; type TMyList = class(TObject) private FCount : Integer; FList : TStringList; protected function FGetMyData(Index: Integer): TMyData; procedure FPutMyData(Index: Integer; Data: TMyData); public property Count : Integer read FCount; property Data[Index: Integer]: TMyData read FGetMyData write FPutMyData; function AddData(Data: TMyData) : Integer; procedure Clear(); procedure Delete(Index: Integer); function Find(const Sn : String; var FindIndex : Integer) : Boolean; constructor Create(); destructor Destroy(); override; end; var Form1: TForm1; implementation {$R *.dfm} { TMyData } constructor TMyData.Create(const RowData: String); var T : TStringList; begin // RowData format : A123456789 小明 台灣省 FSn := ''; FName := ''; FAddr := ''; // extract filed by ' '(space) T := TStringList.Create; try if (ExtractStrings([' '], [' '], PChar(RowData), T) <> 3) then raise Exception.Create('TMyData rowdata format error.'); FSn := T[0]; FName := T[1]; FAddr := T[2]; finally T.Free; end; end; { TMyList } function TMyList.AddData(Data: TMyData): Integer; begin Result := FList.AddObject(Data.Sn, Data); FCount := FList.Count; end; procedure TMyList.Clear; {$IFDEF Delphi2009} begin FList.Clear; {$ELSE} begin while (FCount > 0) do Self.Delete(FCount - 1); {$ENDIF} end; constructor TMyList.Create; begin FList := TStringList.Create; FList.Duplicates := dupAccept; FList.Sorted := True; {$IFDEF Delphi2009} FList.OwnsObjects := True; {$ENDIF} end; procedure TMyList.Delete(Index: Integer); begin if (Index > FCount) then Exit; try {$IFNDEF Delphi2009} FList.Objects[Index].Free; {$ENDIF} FList.Delete(Index); except end; FCount := FList.Count; end; destructor TMyList.Destroy; begin FList.Clear; FList.Destroy; inherited; end; function TMyList.Find(const Sn : String; var FindIndex : Integer) : Boolean; begin Result := FList.Find(Sn, FindIndex); end; function TMyList.FGetMyData(Index: Integer): TMyData; begin Result := FList.Objects[Index] as TMyData; end; procedure TMyList.FPutMyData(Index: Integer; Data: TMyData); begin FList.Objects[Index] := Data; end; { TForm1 } procedure TForm1.btnTestClick(Sender: TObject); var L: TMyList; Data : TMyData; iFind : Integer; sFindSn : String; begin sFindSn := 'B123456790'; L := TMyList.Create; try L.AddData(TMyData.Create('A123456789 小明1 台灣省1')); L.AddData(TMyData.Create('C123456791 小明3 台灣省3')); L.AddData(TMyData.Create('B123456790 小明2 台灣省2')); if L.Find(sFindSn, iFind) then begin Data := L.Data[iFind]; ShowMessage(Format('"%s" at idx %d : "%s" "%s" "%s"', [sFindSn, iFind, Data.Sn, Data.Name, Data.Addr])); end else ShowMessage(Format('"%s" not found.', [sFindSn])); finally L.Free; end; end; [/code] 3. 另外, 文字檔破100mb, 確實有點大, 像上面幾位前輩所提, 移進database並不是件壞事, 而且查詢速度跟資料維護比較方便. 4. 最後, 您這檔案內應該都是重要的個人資料, 台灣個資法已經通過了, 提醒您要小心使用啊!!!~~~ |
Victor4022
中階會員 發表:0 回覆:76 積分:90 註冊:2011-02-20 發送簡訊給我 |
您好, 再補充一點, 剛剛看了Delphi 7的原始碼, TStringList 清單上限如下:
[code delphi] unit Classes; ... ... ... { Maximum TList size } MaxListSize = Maxint div 16; ... ... { TStringList class } TStringItemList = array[0..MaxListSize] of TStringItem; [/code] 應該可以儲存到9位數字, 大約1億3千多萬筆:) |
ac910127
一般會員 發表:7 回覆:27 積分:11 註冊:2009-11-06 發送簡訊給我 |
感謝 Victor4022 大大熱心的回答 !!
讓小弟在學到一種方法。 也感謝大大的個資法的提醒@@ 那是虛擬資料,並非真實個資,應該沒有違法吧!!! 再次謝謝大大 !! ===================引 用 Victor4022 文 章=================== 您好, 再補充一點, 剛剛看了Delphi 7的原始碼, TStringList 清單上限如下: [code delphi] unit Classes; ... ... ... { Maximum TList size } MaxListSize = Maxint div 16; ... ... { TStringList class } TStringItemList = array[0..MaxListSize] of TStringItem; [/code] 應該可以儲存到9位數字, 大約1億3千多萬筆:) |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |