線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:9338
推到 Plurk!
推到 Facebook!

[分享] Split () 分割字串函數

尚未結案
l8939_c
一般會員


發表:9
回覆:15
積分:4
註冊:2005-03-07

發送簡訊給我
#1 引用回覆 回覆 發表時間:2005-05-05 09:34:11 IP:61.220.xxx.xxx 未訂閱
大家好, 小弟常在此地發問, 並且幾乎都能在短時間內獲得滿意的解答 雖然我對 C++ 還是個新手, 但最近模仿 VB 的 Split()函數寫了一個小程式 請大家參考看看, 並且提點改進的意見, 謝謝!    函數功能說明 : 輸入一個 String 字串,及分割字元, 傳回一個 String 陣列            String *Split(String s1,char k1)    //   字串分割    使用範例 : String *aaa=Split("  2   88   789  36      78",' ');            List1->Items->Add(aaa[2]);            ====> 可得到 789    分割字元可以是逗號,分號,空白...... , 但不可以是 '~' 希望有人可以提供更好的方法, 避掉此一限制 另外,回傳的陣列不知有多大, 希望有人有辦法解決    以下是程式碼, 麻煩幫我試試是否有 bug, 或是是否能更簡潔
String *Split(String s1,char k1)    //   字串分割
{
  if (k1==' ') {
    String s3;s3=s1.Trim();
    for (int i=2;iList1->Items->Add(s3);
    s1=s3;k1='~';
  }
  
  int n1=1;               // 計算資料欄位數
  for (int i=1;i<=s1.Length();i  ) {
    if (s1.SubString(i,1)==k1) n1=n1 1;
  }
  String *s2 = new String[n1];int ss1=1,ss2=0;
  int j;
  for (int i=0;i    engineer
        
------
engineer
kagaya
中階會員


發表:74
回覆:175
積分:59
註冊:2002-12-28

發送簡訊給我
#2 引用回覆 回覆 發表時間:2005-05-05 11:57:37 IP:210.200.xxx.xxx 未訂閱
這個函式我也常用.改成下面那樣就沒有~的問題了 用法 String s1="12,34,56,78"; String *s2=Split(s1,","); t1->Text=s2[1]; 至於陣列的長度.我也不知道該怎麼抓=.= String *Split(String str,String dot) //字串分割 { int i=0,j=1,dot_len=dot.Length(); String str_temp=str; while((i=str_temp.Pos(dot))>0){ j ; str_temp=str_temp.SubString(i dot_len,str_temp.Length()); } String *str_arr=new String[j]; if(j==1){ str_arr[0]=str; }else{ j=0; String str_temp=str; while((i=str_temp.Pos(dot))>0){ str_arr[j]=str_temp.SubString(1,i-1); j ; str_temp=str_temp.SubString(i dot_len,str_temp.Length()); } str_arr[j]=str_temp; } return str_arr; }
------
KUSO 無處不在
l8939_c
一般會員


發表:9
回覆:15
積分:4
註冊:2005-03-07

發送簡訊給我
#3 引用回覆 回覆 發表時間:2005-05-05 12:21:09 IP:61.220.xxx.xxx 未訂閱
kagaya 你好    感謝你的建議,不過你的函數再處理分隔字元為空白時會有問題 因為很多情況資料欄位間的空白,不是只有一個 可能是兩個欄位間隔了3,4個空白 而此時只能算兩個欄位,不能因為用了3個空白就判斷有4個欄位 例: String *a=Split2("  2   88   789    36      78,",' '); 答案應該是 a[0]=2  a[1]=88 a[2]=789 ...... 用您的程式會結果會有問題    而我的程式有'~'限制, 就是專門用來處理空白分隔符號的 它將連續空白中的第一個空白,以 '~' 取代 以下再以 '~' 作分隔符號, 來區分欄位    
------
engineer
kagaya
中階會員


發表:74
回覆:175
積分:59
註冊:2002-12-28

發送簡訊給我
#4 引用回覆 回覆 發表時間:2005-05-05 12:27:59 IP:210.200.xxx.xxx 未訂閱
我測了.空白照樣可以分啊.測試如下 String s1="2 88 789 36 78"; String *s2=Split(s1," "); t1->Text=s2[1]; 會顯示88
------
KUSO 無處不在
kagaya
中階會員


發表:74
回覆:175
積分:59
註冊:2002-12-28

發送簡訊給我
#5 引用回覆 回覆 發表時間:2005-05-05 12:31:39 IP:210.200.xxx.xxx 未訂閱
陣列的長度.我看別人的寫法是 sizeof(s2)/sizeof(s2[0]) 不過前提是每個元素的長度要一樣 所以有大大可以解惑一下嗎? 謝謝
------
KUSO 無處不在
l8939_c
一般會員


發表:9
回覆:15
積分:4
註冊:2005-03-07

發送簡訊給我
#6 引用回覆 回覆 發表時間:2005-05-05 15:12:07 IP:61.220.xxx.xxx 未訂閱
kagaya 你好,    你的程式處理 String s1="2 88 789 36 78"; 的確正常    但請將中間分隔"空白"符號改為不一定是一個    例如 : 2 的前面留 2 個空白         2 和 88 中間留 3 個空白       88 和 789 中間留 3 個空白       .........    以此類推    以肉眼來看,連續空白與單一空白一樣, 只會分隔兩筆資料 麻煩你執行看看, 結果是否正常    另外, sizeof(s2)/sizeof(s2[0]) 的問題 由於我們宣告的是指標(*),所以即使元素的長度一樣, 也沒用 我將我的函數做了一點變動, 傳回的陣列,第0個元素即為陣列數目 我想大概就只能做到這樣了吧 用法 :    String *aaa=Split("  2   88   789  36      78  ,",' ');   for (int i=1;i<=aaa[0].ToInt();i++){     List1->Items->Add(aaa[i]);   }    程式碼如下 :    String *Split(String s1,char k1)    //   字串分割 {   if (k1==' ') {     String s3;s3=s1.Trim();     for (int i=2;i
------
engineer
kagaya
中階會員


發表:74
回覆:175
積分:59
註冊:2002-12-28

發送簡訊給我
#7 引用回覆 回覆 發表時間:2005-05-06 14:33:56 IP:210.200.xxx.xxx 未訂閱
把陣列長度放在第一個元素.這個方法蠻不錯的    至於不同長度的空白.通常我會先做處理再切割 例如 replace(str,"   ","~"); replace(str,"  ","~"); replace(str," ","~"); split(str,"~"); 這是我個人直覺式的做法
------
KUSO 無處不在
kagaya
中階會員


發表:74
回覆:175
積分:59
註冊:2002-12-28

發送簡訊給我
#8 引用回覆 回覆 發表時間:2005-05-06 14:35:56 IP:210.200.xxx.xxx 未訂閱
引言: 把陣列長度放在第一個元素.這個方法蠻不錯的 至於不同長度的空白.通常我會先做處理再切割 例如
replace(str,"   ","~");
replace(str,"  ","~");
replace(str," ","~");
split(str,"~");
這是我個人直覺式的做法 < face="Verdana, Arial, Helvetica">
------
KUSO 無處不在
supman
尊榮會員


發表:29
回覆:770
積分:924
註冊:2002-04-22

發送簡訊給我
#9 引用回覆 回覆 發表時間:2005-05-07 01:40:24 IP:203.204.xxx.xxx 未訂閱
您好: 剛剛解答另一題,弄了一各偷懶的切割法,您參考參考.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TStringList *List;
List=new TStringList;
AnsiString S="1,,,,,,,2";//不管你中間有幾各關鍵字,他都識為只有兩各元素
S=StringReplace(S,",","\r\n",TReplaceFlags()<Text=S;
for (int i=0;iCount-1;i  )
 {
  if (List->Strings[i]=="")
   {
    List->Delete(i);
    i--;
   }
 }
List->Free();
}
上面是利用TStringList特性作解析,可以符合您所有的需求,另外小弟的建議是不知道元素,最好是用TStringList來傳,您會減少一些不必要的麻煩.
RedSnow
版主


發表:79
回覆:1322
積分:845
註冊:2003-12-15

發送簡訊給我
#10 引用回覆 回覆 發表時間:2005-05-07 12:29:31 IP:218.19.xxx.xxx 未訂閱
原先我也做過類似的 Split 動作,作法上都大同小異,我以自己的作法修改 l8939_c 的範例如下:
String ReString(String ss, char k1)  // 重整字串
{
    String s1;
    if (k1 == ' ') {
        s1 = ss.Trim();  // 去除首尾的空白字元
        // 下列兩行敘述係將字串內有兩個空白字元之處壓縮成一個,
        // 做兩次是因為避免資料內有碰到超過兩個以上的單數空白字元,
        // (如有三個空白字元,第一次壓縮會變成兩個,因此作兩次較為保險)
        s1 = StringReplace(s1, "  ", " ", TReplaceFlags()<Lines->Add(aaa[i]);
    }
}
上述係因應本篇的討論與簡化自身範例而寫成數個副程式,如果排除前述的基礎,直接修改 l8939_c 範例碼的話,可簡化如下:
String *Split(String ss,char k1)  // 字串分割
{
    String s1;
    if (k1 == ' ') {
        s1 = ss.Trim();
        s1 = StringReplace(s1, "  ", " ", TReplaceFlags()<
以上做法應該可以解決不定數空白字元的分隔問題,同時也可避開 l8939_c 所提的限制,大家參考一下吧。
l8939_c
一般會員


發表:9
回覆:15
積分:4
註冊:2005-03-07

發送簡訊給我
#11 引用回覆 回覆 發表時間:2005-05-09 14:57:07 IP:61.220.xxx.xxx 未訂閱
RedSnow 您好,    您的程式下半段蠻精簡的,但我測試後發現對於3個空白以上的間隔符號, 仍有問題, 我將您的程式修改如下, 上半段是處理空白間隔符號, 下半段是參考您的寫法, 程式還算精簡, 也可避開 '~' 的限制了    
 
String *Split2(String ss,char k1)  // 字串分割
{
  if (k1==' ') {                         // 若分隔字元為空白時,先對字串做處理
    String s3;ss=ss.Trim();             //  先去除頭尾空白
    for (int i=1;i<=ss.Length();i  )
    {
      if (ss.SubString(i,1)!=' ') {
        s3=s3 ss.SubString(i,1);
      }else{
        if (ss.SubString(i-1,1)!= ' ') s3=s3 ss.SubString(i,1);
      }
    }
    ss=s3;
  }
  
  int n1 = 1;                              // 計算資料總筆數
  for (int i=1; i<=ss.Length(); i  ) {
    if (ss.SubString(i, 1) == k1) n1  ;
  }
  String *s2 = new String[n1 1];int pos;
  s2[0]=n1;      for (int i=1; i<=n1; i  ) {
    pos = ss.Pos(k1);
    if(pos == 0){
      s2[i] = ss;
    }else{
      s2[i] = ss.SubString(1, pos-1);
      ss = ss.SubString(pos 1, ss.Length()-1);
    }
  }
  return s2;
}
用法如下 :
 
  String *s2=Split2(" 1  2   3    4     5 ",' ');
  for (int i=1;i<=s2[0].ToInt();i  ){
    List1->Items->Add(a9[i]);      // s2[0] 是此陣列的最大範圍
  }
supman 您好 : 您的程式更為精簡,顯然 TStringList 是蠻方便的, 不過您並未處理"多個空白"分隔符號的問題, 因此我將您的程式加了幾行處理"多個空白"分隔符號
 
 TStringList *Split1(String ss,char k1)  // 字串分割
{
  if (k1==' ') {                       // 若分隔字元為空白時,先對字串做處理
    String s3;ss=ss.Trim();            //  先去除頭尾空白
    for (int i=1;i<=ss.Length();i  )
    {
      if (ss.SubString(i,1)!=' ') {
        s3=s3 ss.SubString(i,1);
      }else{
        if (ss.SubString(i-1,1)!= ' ') s3=s3 ss.SubString(i,1);
      }
    }
    ss=s3;
  }
  
  TStringList *List;
  List=new TStringList;
  ss=StringReplace(ss,k1,"\r\n",TReplaceFlags()<Text=ss;      return  List;
}
用法如下 :
 
 TStringList *s1=Split1("1,2,3,4,5,",',');  // 字串分割 
 for (int i=0;iCount;i  ){
   List1->Items->Add(s1->Strings[i]);
 } 
不過小弟發現仍有一問題, 如上例, 處理 "1,2,3,4,5," 時,以逗號做分隔, 結果應該是有6個欄位,5的後面還有一個空字串才對,但此程式只會輸出5個欄位,最後一個空字串無法察覺, 利用 String 所寫的程式則無此bug, 不知您是否有辦法解決 ?
------
engineer
RedSnow
版主


發表:79
回覆:1322
積分:845
註冊:2003-12-15

發送簡訊給我
#12 引用回覆 回覆 發表時間:2005-05-09 20:02:23 IP:61.140.xxx.xxx 未訂閱
l8939_c 您好:    呵呵~不好意思,我老實招了....我在空白數量的計算上有盲點,在此向您及各位看倌致歉,我將原先那段縮減空白字元的敘述修改如下後,應該就正確了:
String *Split(String ss,char k1)
{
    String s1;
    if (k1 == ' ') {
        s1 = ss.Trim();
        while (s1.Pos("  ") != 0) {  // 檢查字串是否含有連續兩個空白字元?
            s1 = StringReplace(s1, "  ", " ", TReplaceFlags()<
    }else{
        s1 = ss;
    }        int n1 = 1;
    for (int i=1; i<=s1.Length(); i  ) {
        if (s1.SubString(i, 1) == k1) n1  ;
    }        String *s2 = new String[n1];
    int pos;
    String tmp;
    for (int i=0; i
supman
尊榮會員


發表:29
回覆:770
積分:924
註冊:2002-04-22

發送簡訊給我
#13 引用回覆 回覆 發表時間:2005-05-09 20:34:54 IP:203.204.xxx.xxx 未訂閱
您好: 不曉得您有實際去玩過我那範例嬤?他的確可以處理一個空白以上的問題. 以下我改寫加上範例,您測試看看.
TStringList *CustStr(AnsiString S,AnsiString Key)
{
TStringList *List;
List=new TStringList;
S=StringReplace(S,Key,"\r\n",TReplaceFlags()<Text=S;
for (int i=0;iCount-1;i  )
 {
  if (List->Strings[i]=="")
   {
    List->Delete(i);
    i--;
   }
 }
return List;
}    void __fastcall TForm1::Button1Click(TObject *Sender)
{
TStringList *List;
//List=CustStr("1,,,,,,,2,",",");//不管你中間有幾各關鍵字,他都識為只有兩各元素
//List=CustStr("1    23     3222  333332"," ");//使用處理多個空白也可以
List=CustStr("1~~23~~3222~~333332","~~");//也可使用關鍵字是一個Byte以上
List1->Items->Assign(List);
List->Free();
}
不過小弟發現仍有一問題, 如上例, 處理 "1,2,3,4,5," 時,以逗號做分隔, 結果應該是有6個欄位,5的後面還有一個空字串才對,但此程式只會輸出5個欄位,最後一個空字串無法察覺, 利用 String 所寫的程式則無此bug, 不知您是否有辦法解決 ? 假如是這樣表示每出現一個關鍵字就要切割一次,那程式就更簡單了
TStringList *CustStr(AnsiString S,AnsiString Key)
{
TStringList *List;
List=new TStringList;
if (S.SubString(S.Length(),Key.Length())==Key)
 S =Key;
S=StringReplace(S,Key,"\r\n",TReplaceFlags()<Text=S;
return List;
}    void __fastcall TForm1::Button1Click(TObject *Sender)
{
TStringList *List;
List=CustStr("1,2,3,4,5,",",");
List1->Items->Assign(List);
List->Free();
}
發表人 - supman 於 2005/05/09 20:51:57
RedSnow
版主


發表:79
回覆:1322
積分:845
註冊:2003-12-15

發送簡訊給我
#14 引用回覆 回覆 發表時間:2005-05-09 21:58:25 IP:61.140.xxx.xxx 未訂閱
l8939_c 您好:    我原來自定函數的用途並不需要知道陣列大小 (因為是處理固定數量的資料),因此前面的範例都未考慮傳回陣列數量的處理,如果要考慮前述狀況,那麼很顯然的,使用 TStringList 是較為方便的 (在此不考慮效率問題,僅就便利性而言),那麼依您前篇所言,我將兩者方式融合後,改寫成如下所示,以紅色標示的那一行應該可以解決您前一篇向 supman 提出的問題:
TStringList *Split1(String ss, char k1)
{
    String s1;
    if (k1 == ' ') {
        s1 = ss.Trim();
        while (s1.Pos("  ") != 0) {
            s1 = StringReplace(s1, "  ", " ", TReplaceFlags()<
    s1 = s1   "\r\n";        TStringList *List = new TStringList;
    s1 = StringReplace(s1, k1, "\r\n", TReplaceFlags()<Text = s1;        return  List;
}
//----------------------------------------------------    void __fastcall TForm1::Button1Click(TObject *Sender)
{
//  TStringList *s1 = Split1("      2    88   789     36      78       ", ' ');
//  TStringList *s1 = Split1("1,2,3,4,5,,7,", ',');
    TStringList *s1 = Split1(",1,2,3,4,5,", ',');
    for (int i=0; iCount; i  ){
        Memo1->Lines->Add(s1->Strings[i]);
    }
}
l8939_c
一般會員


發表:9
回覆:15
積分:4
註冊:2005-03-07

發送簡訊給我
#15 引用回覆 回覆 發表時間:2005-05-10 09:52:30 IP:61.220.xxx.xxx 未訂閱
supman 您好:    您第一個程式的確可以處理多個空格的問題,而且程式也很精簡 第二個程式的確可以處理逗號分隔時,最後欄位的問題    但是我們最常遇到的"分隔符號"就是逗號或空白,所以我覺得程式應該至少可以同時處理這種兩個問題才實用,您的程式好像都僅針對其中一樣來處理,我覺得這樣好像有點美中不足ㄝ......    < > < > < > < > engineer
------
engineer
l8939_c
一般會員


發表:9
回覆:15
積分:4
註冊:2005-03-07

發送簡訊給我
#16 引用回覆 回覆 發表時間:2005-05-10 10:11:30 IP:61.220.xxx.xxx 未訂閱
RedSnow 您好 : 您的程式處理空白分隔符號的方法,蠻有效率的 不過我發現處理類似 s1=",2,3,4,5," 這種字串時 照理說應該回傳6個欄位,頭尾都有空字串 但執行的結果都僅會回傳5個欄位,尾部空字串不見了 麻煩你試試看,是否是我測試有問題 記得請用 s1=",2,3,4,5," , 最後逗號與"之間不要留空白 engineer
------
engineer
supman
尊榮會員


發表:29
回覆:770
積分:924
註冊:2002-04-22

發送簡訊給我
#17 引用回覆 回覆 發表時間:2005-05-10 19:25:05 IP:203.204.xxx.xxx 未訂閱
您好: 但是我們最常遇到的"分隔符號"就是逗號或空白 不太董您上面這句話的意思? 請舉例一下傳入什麼資料進去時是無法解析的?然後您要的結果是什麼?    我根據您最後的舉例再弄一各範例
TStringList *CustStr(AnsiString S,AnsiString Key)
{
TStringList *List;
List=new TStringList;
if (S.SubString(S.Length(),Key.Length())==Key)
 S =Key;
S=StringReplace(S,Key,"\r\n",TReplaceFlags()<Text=S;
for (int i=0;iCount-1;i  )
 {
  if ((List->Strings[i]=="") &&  (i!=0))
   {
    List->Delete(i);
    i--;
   }
 }
return List;
}    void __fastcall TForm1::Button1Click(TObject *Sender)
{
TStringList *List;
List=CustStr(",2,3,,,4,5,",",");//第一各元素與最後一個元素會是空白的,而中間額外多出來的","則不會被視為一個元素而被刪除.
List1->Items->Assign(List);
List->Free();
}
基本上資料結構中與您要分割的關鍵字並存,在原始定義資料時除非是有規則,不然資料的定義就有問題了. 發表人 - supman 於 2005/05/10 19:26:23
RedSnow
版主


發表:79
回覆:1322
積分:845
註冊:2003-12-15

發送簡訊給我
#18 引用回覆 回覆 發表時間:2005-05-10 21:05:07 IP:219.137.xxx.xxx 未訂閱
l8939_c 您好:    以下是我測試時使用的程式碼:
TStringList *Split1(String ss, char k1)
{
    String s1;
    if (k1 == ' ') {
        s1 = ss.Trim();
        while (s1.Pos("  ") != 0) {
            s1 = StringReplace(s1, "  ", " ", TReplaceFlags()<List->Text = s1;  // (1) 查看 List->Text 內容        return  List;
}
//----------------------------------------------------    void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TStringList *s1 = Split1(",1,2,3,4,5,", ',');
    Memo1->Lines->Add("---- ("+IntToStr(s1->Count)+") ----");  // (2) 查看 s1 屬性
    for (int i=0; iCount; i++){
        Memo1->Lines->Add(IntToStr(i)+" = ["+s1->Strings[i]+"]");
    }
    Memo1->Lines->Add("---- end of data ----");
}  // (3) 執行後結果
以下則是以除錯模式執行結果的截圖,截圖編號對應上述程式碼以紅色標示之處: 圖 (1) 顯示出在自定函式中,List 的內容,共有 7 組 "\r\n" 換行符號: 圖 (2) 顯示出在呼叫 Split1() 函式之後,s1 的 Count 數為 7: 圖 (3) 顯示出 s1 內容輸出至 Memo1 後的結果,s1 第 0 與 6 項的字串值均為 NULL: 您參考一下。 發表人 - RedSnow 於 2005/05/10 21:10:46
系統時間:2024-04-26 0:39:16
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!