如何快速取出字串中的某一段子字串 ?? |
答題得分者是:aquarius
|
ANDY8C
資深會員 發表:114 回覆:582 積分:299 註冊:2006-10-29 發送簡訊給我 |
在字串的分析上,常會讀取某一段子字串的需求 #1111#22#333#4444#55555#6#7#88###ABCD........... 我的需求是要取字串中的某一小段字串,例如:要取第四個分隔符號後的字串 4444,
我的做法是由左讀到右,當讀取到第四個 # 後,才取出 4444 ,感覺不是很好的寫法
萬一要讀第 nnn 個分隔符號後的字串 ,那程式會很浪費時間的..... 不知網友有更精簡的寫法嗎 ? 以下是我部分的程式片段,大家集思廣義.... // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 分割及取出 TEC Command 字串
// instr ==> 要處理的原始字串,最後的字元最好都加原始分割字元
// Old_Pstr ==> 原始分割字元
// New_Pstr ==> 新的分割替代字元(為了完整取出原始字串)
// Get_tag_no ==> 要第幾個取出 Tag ,本分割字元左邊算來第幾個
// tag_len ==> 該取出 Tag 字串長度
// 傳回值 : 某一個特定的分隔字元後的那一串文字
// 若無此 tag_no 或超過實際 tag_no 最大數值, 則傳回 Error
function Parse_Substring( inStr:String; Old_Pstr:string; New_Pstr:string; Get_tag_no:Integer; Get_tag_len:Integer):string;
var
tag_str : string
tag_no,j,nPos_now,nPos_next ,ni : Integer;
begin
// 傳入變數 Pstr 的長度,不可以大於 sParse 的長度
tag_no := 0;
nPos_next := 0;
// 判斷最後的字元,是否為分隔字元,不是的話,自動附加上,方便程式計算
ni := length(Old_Pstr); if not (Old_Pstr = copy( instr, (length(instr)-ni) 1,ni)) then
instr := instr Old_Pstr; while pos(Old_Pstr,inStr) > 0 do
begin
nPos_now := pos(Old_Pstr,inStr);
// 不同長度, Old_Pstr 判斷
if (nPos_now > 0) and (copy(instr,nPos_now,ni)=Old_Pstr) then
begin
tag_str := copy(inStr,nPos_Next 1,nPos_now-nPos_Next-1); if tag_no = Get_tag_no then
begin
RESULT := copy(tag_str,1,Get_tag_len); // 取需要的字串長度
exit;
end; nPos_Next := nPos_now ni-1;
tag_no := tag_no 1;
end; // 分割字元替換處理,可替換一個字元以上
for j := 1 to length(Old_Pstr) do
inStr[nPos_now j-1] := New_Pstr[j];
end; RESULT := 'STRING ERROR : (0000) 參數的內容太少或型態錯誤 !! ' #13#10 '(Parse)->' instr #13#10; // 有錯誤發生 end;
= = = == = = = = = = = = = == == = = = = = = = = = = = = = = = =
不知網友有更精簡的寫法嗎 ? --------------------------------
這一網站,真的不錯!! 發表人 - ANDY8C 於 2004/12/08 13:50:49 發表人 - ANDY8C 於 2004/12/08 14:08:43 發表人 - ANDY8C 於 2004/12/08 14:09:36
------
--------------------------------------- 偶爾才來 KTOP ,交流條碼問題,在 FB [條碼標籤達人] 社團留言,感恩. |
shieh2700
高階會員 發表:0 回覆:127 積分:100 註冊:2002-06-13 發送簡訊給我 |
procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage( Split( '#1111#22#333#4444#55555#6#7#88###ABCD', '#' )[4] ); end; function Split(s, delimiter: string): TStrings; var rs : TStrings; begin rs := TStringList.Create; rs.Text := StringReplace( s, delimiter, #13, [rfReplaceAll, rfIgnoreCase] ); Result := rs; end; |
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
shieh2700,你好!你的程式碼可以Work,但是有一點點小問題,就是Split回傳的TStringList,每次使用後都沒有被釋放掉,個人做了些許修改
procedure TForm1.Button1Click(Sender: TObject); var S: TStrings; begin S := Split('#1111#22#333#4444#55555#6#7#88###ABCD','#'); try if Assigned(S) then ShowMessage(S[4]); finally FreeAndNil(S); end; end; function Split(s, delimiter: string): TStrings; begin Result := TStringList.Create; Resultrs.Text := StringReplace(s,delimiter,#13,[rfReplaceAll, rfIgnoreCase]); end; |
shieh2700
高階會員 發表:0 回覆:127 積分:100 註冊:2002-06-13 發送簡訊給我 |
|
aquarius
資深會員 發表:3 回覆:347 積分:330 註冊:2003-05-21 發送簡訊給我 |
可以用 strpos 加速. 至於用 TStrings, 看起來簡潔, 但運算時間反而大增, 因為還要做一大堆create物件的相關動作. 參考下列的程式碼.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 分割及取出 TEC Command 字串 // instr ==> 要處理的原始字串,最後的字元最好都加原始分割字元 // Old_Pstr ==> 原始分割字元 // New_Pstr ==> 新的分割替代字元(為了完整取出原始字串) // Get_tag_no ==> 要第幾個取出 Tag ,本分割字元左邊算來第幾個 // tag_len ==> 該取出 Tag 字串長度 // 傳回值 : 某一個特定的分隔字元後的那一串文字 // 若無此 tag_no 或超過實際 tag_no 最大數值, 則傳回 Error function Parse_Substring( inStr:String; Old_Pstr:string; New_Pstr:string; Get_tag_no:Integer; Get_tag_len:Integer):string; var tag_str : string ; tag_no,j,nPos_now,nPos_next ,ni : Integer; begin // 傳入變數 Pstr 的長度,不可以大於 sParse 的長度 tag_no := 0; nPos_next := 0; // 判斷最後的字元,是否為分隔字元,不是的話,自動附加上,方便程式計算 ni := length(Old_Pstr); if not (Old_Pstr = copy( instr, (length(instr)-ni) 1,ni)) then instr := instr Old_Pstr; while pos(Old_Pstr,inStr) > 0 do begin nPos_now := pos(Old_Pstr,inStr); // 不同長度, Old_Pstr 判斷 if (nPos_now > 0) and (copy(instr,nPos_now,ni)=Old_Pstr) then begin tag_str := copy(inStr,nPos_Next 1,nPos_now-nPos_Next-1); if tag_no = Get_tag_no then begin RESULT := copy(tag_str,1,Get_tag_len); // 取需要的字串長度 exit; end; nPos_Next := nPos_now ni-1; tag_no := tag_no 1; end; // 分割字元替換處理,可替換一個字元以上 for j := 1 to length(Old_Pstr) do inStr[nPos_now j-1] := New_Pstr[j]; end; RESULT := 'STRING ERROR : (0000) 參數的內容太少或型態錯誤 !! ' #13#10 '(Parse)->' instr #13#10; // 有錯誤發生 end; function Split(s, delimiter: string): TStrings; begin Result := TStringList.Create; Result.Text := StringReplace(s,delimiter,#13,[rfReplaceAll, rfIgnoreCase]); end; function NewParse_Substring(inStr:String; Old_Pstr:string; Get_tag_no:Integer; Get_tag_len:Integer):string; var pc, pcNext, pcRet, pcTag : pchar ; i, iLen : integer ; begin pc:=pchar(inStr) ; pcRet:=pc ; pctag:=pchar(Old_Pstr) ; for i:=1 to Get_tag_no do begin pcRet:=StrPos(pc,pcTag) ; if pcRet=nil then begin Result:='STRING ERROR : (0000) 參數的內容太少或型態錯誤 !! ' #13#10 '(Parse)->' instr #13#10; // 有錯誤發生 exit ; end ; pc:=pcRet 1 ; end ; pcNext:=StrPos(pc,pcTag) ; if pcNext=nil then pcNext:=StrEnd(pchar(Old_Pstr)) ; i:=DWORD(pcNext)-DWORD(pc) ; Result:=copy(strpas(pc),1,i) ; end ; procedure TForm1.Button1Click(Sender: TObject); var s, sRet : string ; i : integer ; dw : DWORD ; begin s:='#1111#22#333#4444#55555#6#7#88###ABCD' ; Memo1.clear ; dw:=GetTickCount ; for i:=0 to 99999 do begin sRet:=NewParse_Substring(s,'#',i mod 13,10) ; end ; Memo1.Lines.Add(format('Time=%d',[GetTickCount-dw])) ; end; procedure TForm1.Button2Click(Sender: TObject); var s, sRet : string ; i : integer ; dw : DWORD ; begin s:='#1111#22#333#4444#55555#6#7#88###ABCD' ; Memo1.clear ; dw:=GetTickCount ; for i:=0 to 99999 do begin sRet:=Parse_Substring(s,'#','-',i mod 13,10) ; end ; Memo1.Lines.Add(format('Time=%d',[GetTickCount-dw])) ; end; procedure TForm1.Button3Click(Sender: TObject); var sl : TStrings ; s, sRet : string ; i : integer ; dw : DWORD ; begin s:='#1111#22#333#4444#55555#6#7#88###ABCD' ; Memo1.clear ; dw:=GetTickCount ; for i:=0 to 99999 do begin sl:=Split(s,'#'); try if Assigned(sl) then if (i mod 13)
------
水瓶男的blog: http://791909.blogspot.com |
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
如果是...
procedure TForm1.Button3Click(Sender: TObject); var sl : TStrings ; s, sRet : string ; i : integer ; dw : DWORD ; begin s:='#1111#22#333#4444#55555#6#7#88###ABCD' ; Memo1.clear ; dw:=GetTickCount ; sl:=Split(s,'#'); try if Assigned(sl) then if s1.Count > 0 then for i:=0 to 99999 do sRet:=sl[i mod sl.count]; finally FreeAndNil(sl); end ; Memo1.Lines.Add(format('Time=%d',[GetTickCount-dw])) ; end; 我覺得shieh2700做法依舊可取的,見仁見智. |
shieh2700
高階會員 發表:0 回覆:127 積分:100 註冊:2002-06-13 發送簡訊給我 |
個人認為兩種做法各有其優缺點, 所以寫了支程式來模擬四種狀況, 突顯其間之差異: 【執行結果】
模擬四種狀況, 取十組資料, 每組各跑一萬次, 計算此兩種處理方式的處理時間
測試資料=00#11#22#33#44#55#66#77#88#99#00#11#22#33#44#55#66#77#88#99#00#11#22#33
#44#55#66#77#88#99#00#11#22#33#44#55#66#77#88#99#00#11#22#33#44#55#66#77#88#99#
分隔字元=#
模擬狀況一:只取第五個欄位
SubString:60,50,50,50,50,40,50,40,50,51,
Split :1822,1873,1903,1882,1893,1873,1872,1883,1873,1862,
模擬狀況二:取得所有欄位
SubString:6350,6319,6329,6329,6339,6369,6399,6329,6320,6399,
Split :1792,1843,1833,1842,1753,1732,1753,1752,1773,1752,
模擬狀況三:只取前二十個欄位
SubString:1372,1362,1362,1382,1382,1392,1362,1382,1362,1382,
Split :1773,1762,1873,1903,1902,1893,1893,1873,1872,1893,
模擬狀況四:只取後二十個欄位
SubString:3615,3585,3636,3595,3585,3625,3585,3605,3606,3585,
Split :1882,1863,1863,1863,1862,1803,1762,1773,1873,1882, 【測試程式】
program SplitTest; {$APPTYPE CONSOLE} uses SysUtils, classes, windows; //jow function Split(s, delimiter: string): TStrings; begin Result := TStringList.Create; Result.Text := StringReplace(s,delimiter,#13,[rfReplaceAll, rfIgnoreCase]); end; //aquarius function NewParse_Substring(inStr:String; Old_Pstr:string; Get_tag_no:Integer; Get_tag_len:Integer):string; var pc, pcNext, pcRet, pcTag : pchar ; i, iLen : integer ; begin pc:=pchar(inStr) ; pcRet:=pc ; pctag:=pchar(Old_Pstr) ; for i:=1 to Get_tag_no do begin pcRet:=StrPos(pc,pcTag) ; if pcRet=nil then begin Result:='STRING ERROR : (0000) 參數的內容太少或型態錯誤 !! ' #13#10 '(Parse)->' instr #13#10; // 有錯誤發生 exit ; end ; pc:=pcRet 1 ; end ; pcNext:=StrPos(pc,pcTag) ; if pcNext=nil then pcNext:=StrEnd(pchar(Old_Pstr)) ; i:=DWORD(pcNext)-DWORD(pc) ; Result:=copy(strpas(pc),1,i) ; end ; //處理狀況一:只取第五欄位 procedure test1(s,d:string); var sRet : string ; ss:TStrings; i,j : integer ; dw : DWORD ; begin Write(#9 'SubString:'); for i:=0 to 9 do begin dw:=GetTickCount ; for j:=0 to 9999 do sRet:=NewParse_Substring(s,d,5,10) ; Write(format('%d,',[GetTickCount-dw])); end; WriteLn(''); Write(#9 'Split :'); for i:=0 to 9 do begin dw:=GetTickCount ; for j:=0 to 9999 do begin ss:=Split(s,d) ; try if Assigned(ss) then if ss.Count>=5 then sRet:=ss[5]; finally FreeAndNil(ss); end ; end; Write(format('%d,',[GetTickCount-dw])); end; WriteLn(''); end; //處理狀況二:取得所有欄位 procedure test2(s,d:string); var sRet : string ; ss:TStrings; i,j,k : integer ; dw : DWORD ; begin Write(#9 'SubString:'); for i:=0 to 9 do begin dw:=GetTickCount ; for j:=0 to 9999 do begin for k := 0 to 49 do sRet:=NewParse_Substring(s,d,k,10) ; end; Write(format('%d,',[GetTickCount-dw])); end; WriteLn(''); Write(#9 'Split :'); for i:=0 to 9 do begin dw:=GetTickCount ; for j:=0 to 9999 do begin ss:=Split(s,d) ; try if Assigned(ss) then for k := 0 to 49 do sRet:=ss[k]; finally FreeAndNil(ss); end ; end; Write(format('%d,',[GetTickCount-dw])); end; WriteLn(''); end; //處理狀況三:只取前二十個欄位 procedure test3(s,d:string); var sRet : string ; ss:TStrings; i,j,k : integer ; dw : DWORD ; begin Write(#9 'SubString:'); for i:=0 to 9 do begin dw:=GetTickCount ; for j:=0 to 9999 do begin for k := 0 to 19 do sRet:=NewParse_Substring(s,d,k,10) ; end; Write(format('%d,',[GetTickCount-dw])); end; WriteLn(''); Write(#9 'Split :'); for i:=0 to 9 do begin dw:=GetTickCount ; for j:=0 to 9999 do begin ss:=Split(s,d) ; try if Assigned(ss) then for k := 0 to 19 do sRet:=ss[k]; finally FreeAndNil(ss); end ; end; Write(format('%d,',[GetTickCount-dw])); end; WriteLn(''); end; //處理狀況四:只取後二十個欄位 procedure test4(s,d:string); var sRet : string ; ss:TStrings; i,j,k : integer ; dw : DWORD ; begin Write(#9 'SubString:'); for i:=0 to 9 do begin dw:=GetTickCount ; for j:=0 to 9999 do begin for k := 30 to 49 do sRet:=NewParse_Substring(s,d,k,10) ; end; Write(format('%d,',[GetTickCount-dw])); end; WriteLn(''); Write(#9 'Split :'); for i:=0 to 9 do begin dw:=GetTickCount ; for j:=0 to 9999 do begin ss:=Split(s,d) ; try if Assigned(ss) then for k := 30 to 49 do sRet:=ss[k]; finally FreeAndNil(ss); end ; end; Write(format('%d,',[GetTickCount-dw])); end; WriteLn(''); end; var testData: string; begin // Insert user code here testData := '00#11#22#33#44#55#66#77#88#99#'; testData := testData testData testData testData testData; Writeln(#13 '模擬四種狀況, 取十組資料, 每組各跑一萬次, 計算此兩種處理方式的處理時間'); Writeln(#13 '測試資料=' testData); Writeln(#13 '分隔字元=#'); Writeln(#13 '模擬狀況一:只取第五個欄位'); test1(testData,'#'); Writeln(#13 '模擬狀況二:取得所有欄位'); test2(testData,'#'); Writeln(#13 '模擬狀況三:只取前二十個欄位'); test3(testData,'#'); Writeln(#13 '模擬狀況四:只取後二十個欄位'); test4(testData,'#'); WriteLn('處理完成...'); readln; end. |
aquarius
資深會員 發表:3 回覆:347 積分:330 註冊:2003-05-21 發送簡訊給我 |
引言: 如果是...改成這樣當然 TStrings 會比較快, 因為省去了重新 Parsing 的時間, 就直接索引到需要的資料. 但是在實際運用時, 會呼叫 Parsing 一般都是用來讀入一個大的文字檔, 例如 .CSV檔, 再取出資料處理. 那麼 TStrings 就必需不斷的重新 Create / Free. ...Aquariusprocedure TForm1.Button3Click(Sender: TObject); var sl : TStrings ; s, sRet : string ; i : integer ; dw : DWORD ; begin s:='#1111#22#333#4444#55555#6#7#88###ABCD' ; Memo1.clear ; dw:=GetTickCount ; sl:=Split(s,'#'); try if Assigned(sl) then if s1.Count > 0 then for i:=0 to 99999 do sRet:=sl[i mod sl.count]; finally FreeAndNil(sl); end ; Memo1.Lines.Add(format('Time=%d',[GetTickCount-dw])) ; end; 我覺得shieh2700做法依舊可取的,見仁見智.
------
水瓶男的blog: http://791909.blogspot.com |
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
引言:
--------------------------------------------------------------------------------
改成這樣當然 TStrings 會比較快, 因為省去了重新 Parsing 的時間, 就直接索引到需要的資料. 但是在實際運用時, 會呼叫 Parsing 一般都是用來[讀入一個大的文字檔, 例如 .CSV檔, 再取出資料處理. 那麼 TStrings 就必需不斷的重新 Create / Free.
-------------------------------------------------------------------------------- 個人也是認為這樣,Parsing動作一次到位,並且我覺得以TStrings的方式處理檔案也相當適合的,
function Split(s, delimiter: string): TStrings; begin Result := TStringList.Create; Result.Text := StringReplace(s,delimiter,#13,[rfReplaceAll, rfIgnoreCase]); end; procedure TForm1.Button1Click(Sender: TObject); var R, S: TStringList begin if FileExists(A_FILENAME)then begin R := TStringList.Create; try R.LoadFromFile(A_FILENAME); S := Split(R.Text); try if Assigned(S) then begin . . . end; finally FreeAndNil(S); end; finally FreeAndNil(R); end; end; end;我覺得程式碼簡潔並且較具彈性,個人淺見,有錯勿怪. |
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
procedure TForm1.Button1Click(Sender: TObject); var R, S: TStringList begin if FileExists(A_FILENAME)then begin R := TStringList.Create; try R.LoadFromFile(A_FILENAME); S := Split(R.Text,'#'); <--更正 try if Assigned(S) then begin . . . end; finally FreeAndNil(S); end; finally FreeAndNil(R); end; end; end;補充: (1)方便回傳資料的存檔 S->SaveToFile(AOTHER_FILENAME); (2)當 Delimiter 不是 '#', 而是 '#' 的狀態下, TStrings 的做法較具彈性. |
shieh2700
高階會員 發表:0 回覆:127 積分:100 註冊:2002-06-13 發送簡訊給我 |
引言:假若是讀取 .CSV 之類的檔案, 我個人的習慣也還是使用 TStrings 來處理, 且不需重新 Create/Free:引言: 如果是...改成這樣當然 TStrings 會比較快, 因為省去了重新 Parsing 的時間, 就直接索引到需要的資料. 但是在實際運用時, 會呼叫 Parsing 一般都是用來讀入一個大的文字檔, 例如 .CSV檔, 再取出資料處理. 那麼 TStrings 就必需不斷的重新 Create / Free. ...Aquariusprocedure TForm1.Button3Click(Sender: TObject); var sl : TStrings ; s, sRet : string ; i : integer ; dw : DWORD ; begin s:='#1111#22#333#4444#55555#6#7#88###ABCD' ; Memo1.clear ; dw:=GetTickCount ; sl:=Split(s,'#'); try if Assigned(sl) then if s1.Count > 0 then for i:=0 to 99999 do sRet:=sl[i mod sl.count]; finally FreeAndNil(sl); end ; Memo1.Lines.Add(format('Time=%d',[GetTickCount-dw])) ; end; 我覺得shieh2700做法依舊可取的,見仁見智. procedure CsvToDB( f: string ); const d: string =','; //Delimiter var r,s:TStrings; i:integer; begin if FileExists(f) then begin r := TStringList.Create; try r.LoadFromFile(f); s := TStringList.Create(); try if r.Count>0 then begin for i := 0 to r.Count-1 do begin s.Text := StringReplace(r[i],d,#13,[rfReplaceAll, rfIgnoreCase]) //執行要處理的動作... end; end; finally FreeAndNil(s); end; finally FreeAndNil(r); end; end; end; |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |