複数セパレータによる文字列分割 – プログラミング – Home

複数セパレータによる文字列分割
 
通知
すべてクリア

[解決済] 複数セパレータによる文字列分割


DOSKOI-PANDA
 DOSKOI-PANDA
(@DOSKOI-PANDA)
ゲスト
結合: 22年前
投稿: 55
Topic starter  

Win2K VC++6.0 SP5 です。

も少し、うまくできないか、という質問です。

文字列を分割する場合、strtok() だと、

strtok(lpszData, , \t);

という具合に、複数のセパレータを指定できますが、
CString::Find() は、検索データが、1文字または部分文字列です。
strData.Find(, \t, 0)だと、strData内から, \t のいずれかではなく、
, \tという文字の連なり(の場所)を探してしまいます。

CString::FindOneOf() だと、文字列の先頭からしか探せんし、セパレータの
連続数が解らんし。

で、CString の派生クラスを使って以下のようにしてます。(長めです)

class CStringEx : public CString
{
public:
CStringEx();
CStringEx(const CString& strData) : CString(strData) {}
virtual ~CStringEx();

int FindEx(const CString& strSeparators, int nStart, int& nCount);
};

int CStringEx::FindEx(
const CString& strSeparators, // セパレータ
int nStart, // 検索開始位置
int& nCount // セパレータ連続数
) {
int nSeparatorCount = strSeparators.GetLength();

// セパレータが見つかる先頭位置を探す
int nMinPos = INT_MAX;
for (int i = 0; i < nSeparatorCount; i++) {
TCHAR c = strSeparators[i];
int nPos = Find(c, nStart);
if (-1 == nPos) continue;
if (nMinPos > nPos) {
nMinPos = nPos;
}
}

if (INT_MAX != nMinPos) { // 見つかった

// セパレータ文字が連続している間繰り返し
int nLength = GetLength();
int nCnt = 0;
for (int nPos = nMinPos; nPos < nLength; nPos++) {
TCHAR c = GetAt(nPos);
int nFind = strSeparators.Find(c);
if (-1 != nFind) { // セパレータ文字数をカウント
nCnt++;
} else {
break;
}
}
nCount = nCnt;
}

return (INT_MAX == nMinPos) ? -1 : nMinPos;
}

で、文字列を分割する関数の方で、

void CTokenDlg::Convert(const CString &strData, const CString& strSeparators,
CStringArray &astrData)
{
int nPrevPos = 0;
int nCount = 0;
CStringEx strDataEx = strData;
int nPos;
while (-1 != (nPos = strDataEx.FindEx(strSeparators, nPrevPos,
nCount))) {
int nCharCount = nPos - nPrevPos;
CString strPart = strData.Mid(nPrevPos, nCharCount);
astrData.Add(strPart);
nPrevPos = nPos + nCount;
}
if (0 == nCount) {
astrData.Add(strData);
}
}

とやると、どうやらうまくいるようであります。

がしかし、もっとうまく(エレガントに短く)やる方法はないものでしょうか?

std::string を使う手もあるかなと思って調べてみると、find_first_of() という
のがあって一瞬期待しましたが、この人は部分文字列内で最初にみつけた
セパレータの位置を返すそうで。(無念)

複数セパレータによる文字列分割なんて割りとありふれた処理だと思うので、
(Excel でテキストファイルを読み込むときとか)どなたかうまいやり方を
ご存知の方、ご教唆願います。


引用未解決
トピックタグ
DOSKOI-PANDA
 DOSKOI-PANDA
(@DOSKOI-PANDA)
ゲスト
結合: 22年前
投稿: 55
Topic starter  

ちなみに strtok() を使って分割する方法は知っております。

for (char* token = strtok(lpszData, , \t);
token;
token = strtok(NULL, , \t) )
{
// token に対する処理
}

でしたっけ?

ですが、まぁせっかくMFCやらSTLやらが使えるので、C++っぽくやる方法は
ないかな? という質問であります。


返信引用
DOSKOI-PANDA
 DOSKOI-PANDA
(@DOSKOI-PANDA)
ゲスト
結合: 22年前
投稿: 55
Topic starter  

ごめんなさい。
MSDN見てると、SpanExcluding()/SpanIncluding()が見つかりました。
以下のようにやるのが今のところ一番コンパクトかな。

void CTokenDlg::Convert(const CString &strData, const CString& strSeparators,
CStringArray &astrData)
{
CString strWork = strData;

int nLength = strWork.GetLength();
while (nLength) {

// 部分文字列取り出し
CString strPart = strWork.SpanExcluding(strSeparators);
astrData.Add(strPart);
nLength -= strPart.GetLength();
strWork = strWork.Right(nLength);

// セパレータ部分取り出し
CString strSeps = strWork.SpanIncluding(strSeparators);
nLength -= strSeps.GetLength();
strWork = strWork.Right(nLength);
}
}

std::string 版も find_first_not_of() というのがありましたので、
それでできそうですね。


返信引用
DOSKOI-PANDA
 DOSKOI-PANDA
(@DOSKOI-PANDA)
ゲスト
結合: 22年前
投稿: 55
Topic starter  

std::string でもできました。

なんか自作自演みたいですみません。

void CTokenDlg::Convert(const string &strData, const string& strSeparators,
vector<string>& astrData)
{
string strWork = strData;

unsigned int nPos = 0;
unsigned int nLength = strWork.length();
while (nLength) {

// 部分文字列取り出し
unsigned int nPartEnd = strWork.find_first_of(strSeparators);
if (string::npos == nPartEnd) {
astrData.push_back(strWork);
break;
}
string strPart = strWork.substr(0, nPartEnd);
astrData.push_back(strPart);
strWork = strWork.substr(nPartEnd, nLength - nPartEnd);
nLength = strWork.length();

// セパレータ部分取り出し
unsigned int nSepsEnd = strWork.find_first_not_of
(strSeparators);
if (string::npos == nSepsEnd) {
break;
}
string strSeps = strWork.substr(0, nSepsEnd);
strWork = strWork.substr(nSepsEnd, nLength - nSepsEnd);
nLength = strWork.length();
}
}


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました