核心元件‎ > ‎

字串分割

這個和 C/C++ 的 wcspbrk 用途一樣,目的都為了做字句切割,但功能更強,主要加強了以下特點:

  • 分割標記不侷限在字元,改為字串,用途更廣。
  • 提供截取和略過兩種功能,互相搭配使用。
  • 若採用截取可得知找到的是哪個標記字串。
  • 可依不同的狀態指定不一樣的 分割 或 略過 識別文字。
  • 以 Unicode 作為處理的基礎,所以也可以處理中文字。
cxxlWcspbrk 元件的功用蠻簡單,即有一個被標記字串(可同時存在多種標記字串)分割的字串來源,cxxlWcspbrk 元件就是用來將這些被分割的子字串一一取出。
 

資料源

要使用 cxxlWcspbrk 須在建構時提供資料源,資料源須是 IWcspbrkSource 這個介面的實作,IWcspbrkSource 是以資料流的方式提供一連串的字元給 cxxlWcspbrk,實作類別須覆寫 IWcspbrkSource 的一個虛擬函數:

// 索取指定長度的字串,回覆實際取得的長度
virtual size_t cxxlFASTCALL GetStr(wchar_t *Dest,size_t Len) = 0; 

cxxlWcspbrk 便是藉由這函數取得資料源的資料,直到取完為止。

  • 內附 UnicodeStr_Source 這個用來包裹 wchar_t 字串的的資料可直接使用,它的做法和以下例子中的 Unicode_Source 一樣。
  • 內附 wiStream_Source,可用來經由 wistream 讀取資料源
 

使用流程

使用端藉由呼叫 cxxlWcspbrk 的 Next() 功能(截取) 或 XNext() 功能(略過),指定識別字串並傳回執行結果,Next() 和 XNext() 宣告如下:

cxxlWcspbrk_Result cxxlFASTCALL Next(const wchar_t *tokens[]);

cxxlWcspbrk_XResult cxxlFASTCALL XNext(const wchar_t *skips[]);

tokens 和 skips 是一個二維的字串,每個字串不得超過 128 個字元,越前面的越先批配,最後須指定 NULL 以作為結尾識別, 例子:

const wchar_t *tokens[] = {L"\r\n",L"\n",L"\r",NULL};

 

cxxlWcspbrk_Result 的定義如下:

struct cxxlWcspbrk_Result
{
  // 符合 tokens 或 NULL 之前的字句片斷 
  std::wstring SubStr; 

  // 符合的標記字串,無符合則 NULL
  UNICODE_String Token;
};

 

cxxlWcspbrk_XResult 的定義如下:

struct cxxlWcspbrk_XResult
{
  // 不符合 skips 或 NULL 之前的字句片斷
  std::wstring SubStr;
 
  // 已讀至 NULL (讀取完了) 為 true
  bool isNULL;
};
  

不能使用於多執行緒

多執行緒直接對同一個 cxxlWcspbrk 物件做存取也太奇怪了,依常理不應有多個執行緒搶著索取字句,必然會弄亂整個資料流的意義,基於此理由,cxxlWcspbrk 並沒做執行緒的同步控制。
 

範例一

這個範例用來清除 c++ 原始程式碼中的註解資料,採用 static 設計模式來操作
狀態編號 意義 情況
1
在註解之外
"/*" = 輸出之前的字串,狀態改為 2
"//" = 輸出之前的字串,狀態改為 3
NULL = 輸出之前的字串,結束     
2
在 /*  */ 之內
"*/" = 狀態改為 1
NULL = 結束   
3
在 // 之後
"\r\n" = 輸出 "\r\n",狀態改為 1
"\r" = 輸出 "\r",狀態改為 1
"\n" = 輸出 "\n",狀態改為 1
NULL = 結束
 
#include <locale>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <SMART_STRING.HPP>
#include <CXXLWCSPBRK.HPP>
using namespace std;
using namespace CxxlMan;
// cxxlWcspbrk 用的資料源
class Unicode_Source:public IWcspbrkSource
{
  UNICODE_String m_StrSrc;
  size_t StrSrcBase;
  // 來自 IWcspbrkSource 須實作的虛擬函數
  size_t cxxlFASTCALL GetStr(wchar_t *Dest,size_t Len)
  {
    size_t l = m_StrSrc.StrLength() - StrSrcBase;

    if(l >= Len)
    {
      for(size_t i = 0; i<Len; ++i)
      {
        Dest[i] = ((const wchar_t*)m_StrSrc)[StrSrcBase + i];
      }
StrSrcBase += Len; return Len; } else { for(size_t i = 0; i < l; ++i) { Dest[i] = ((const wchar_t*)m_StrSrc)[StrSrcBase + i]; } StrSrcBase = m_StrSrc.StrLength(); return l; } }
public:
  Unicode_Source(const CxxlMan::UNICODE_String &StrSrc, ISpirit *spirit = Spirit_Easy)
:cxxlObject(spirit),m_StrSrc(StrSrc)
{
StrSrcBase = 0;
}
};
class ClearRemark
{
  cxxlWcspbrk Wcspbrk;

  wostringstream Dest;
  // 未處放註解內的處理函數
  bool RemOut();
  // 處於 /* */ 內的處理函數
  bool RemIn1();
  // 處於 // 內的處理函數
  bool RemIn2();
  bool (ClearRemark::*funcPtr)();
public:
  // Constructor
  ClearRemark(const UNICODE_String &S)
:Wcspbrk(new Unicode_Source(S))
{
funcPtr = &ClearRemark::RemOut;
}
// Destructor virtual ~ClearRemark() { }
  wstring toDo()
  {
    while((this->*funcPtr)());
    return Dest;
  }
};
 
// 未處於註解內的處理函數
bool ClearRemark::RemOut()
{
  const wchar_t *pTok[] = {L"//",L"/*",NULL};
  cxxlWcspbrk_Result r = Wcspbrk.Next( pTok  );
  Dest << r.SubStr;
  if(r.Token.isNULL())
    return false;
  if(wcscmp(r.Token, L"//") == 0)
    funcPtr = &ClearRemark::RemIn2; 
  else if(wcscmp(r.Token, L"/*") == 0)
    funcPtr = &ClearRemark::RemIn1; 
  return true;
}
// 處於 /* */ 內的處理函數
bool ClearRemark::RemIn1()
{
  const wchar_t *pTok[] = {L"*/",NULL};
  cxxlWcspbrk_Result r = Wcspbrk.Next( pTok  );

  if(r.Token.isNULL())
    return false;
  funcPtr = &ClearRemark::RemOut; 
  return true;
}
// 處於 // 內的處理函數
bool ClearRemark::RemIn2()
{
  const wchar_t *pTok[] = {L"\r\n",L"\n",L"\r",NULL};
  cxxlWcspbrk_Result r = Wcspbrk.Next( pTok  );

  if(r.Token.isNULL())
    return false;
 
  if( r.Token == L"\r\n")
  {
    Dest << L"\r\n";
  }
  else if( r.Token == L"\r")
  {
    Dest << L'\r';
  }
  else
  {
    Dest << L'\n';
  }
  funcPtr = &ClearRemark::RemOut; 
  return true;
}
int main(int argc, char* argv[])
{
  if(argc != 2)
  {
    // 假設被編譯成執行檔名為 clrrem
    cout << "clrrem filename" << endl << endl
         << "  filename 含有註解的 c++ 原始檔名" << endl;
    return 0;
  }
  ifstream fin(argv[1]);
  if(!fin.good())
  {
    cout << "無法開啟指定的來源檔名" << endl;
    return 0;
  }
  stringstream SS;
  SS << fin.rdbuf(); // 取得整個文檔
  fin.close();
  string StrSrc(SS.str());
  setlocale(LC_CTYPE, "");  // 指定系統現正使用中的地區語言做轉換
  UNICODE_String WStrSrc(StrSrc.c_str()); // 轉為 Unicode 碼
  ClearRemark CM(WStrSrc);
  wcout << CM.toDo() << endl;
 
  return 0;
}
 

範例二

以下是 截取 和 略過 搭配使用的例子

#include <locale>
#include <iostream>
#include <CXXLWCSPBRK.HPP>
using namespace std;
using namespace CxxlMan;
 
int main(int argc, char* argv[])
{
  const wchar_t *S = L"test   str 123 ABCD";
  Smart_Ptr<cxxlWcspbrk> Wcspbrk_Ptr = new cxxlWcspbrk(new UnicodeStr_Source(S));
  const wchar_t *Sh1[] = {L" ",NULL}; // 截取用的
  const wchar_t *Sh2[] = {L"str",L" ",NULL}; // 略過用的
  cxxlWcspbrk_Result R = Wcspbrk_Ptr->Next(Sh1); // 截取出 "test"
  wcout << R.SubStr << endl;
  cxxlWcspbrk_XResult XR = Wcspbrk_Ptr->XNext(Sh2); // 略過 "  str "
  wcout << XR.SubStr << endl;
  R = Wcspbrk_Ptr->Next(Sh1); // 截取出 "123"
  wcout << R.SubStr << endl;
}
 
 
 

  
引入檔

CXXLWCSPBRK.HPP 

程式庫

cxxlObject

 
Comments