C 文字列操作 新しいページはコチラ
提供: yonewiki
(→文字列大文字小文字変換) |
(→文字列の検索と置換) |
||
3,758行: | 3,758行: | ||
=='''文字列の検索と置換'''== | =='''文字列の検索と置換'''== | ||
文字列の検索と置換は、メモ帳でCtrl+Fとかで検索するようなものや置換する処理と同じように目的の文字列を探す方法とファイルの検索のようにワイルドカードを使う方法があり、検索して一致した文字列を置き換えるという処理も同じみですが、もうひとつの検索と置換のやり方として、正規表現(Regular Expresion)という手法があります。これはワイルドカードによる指定にさらに文字列の先頭にある特定の文字列があって、さらに特定の文字列にはさまれた部分があって、文字列の最後尾にも特定の文字があったら、その挟まれた文字の先頭グループ1、グループ2、…グループnとして、順次、変換法則にしたがって変換といった複雑な指定が可能になる手法です。正規表現は複雑なこともできるし、単純なこともできる。できるだけ複雑なモノを単純にしてから操作をする手法など、検索と置換には多岐にわたる手法が存在します。また、検索効率をあげる手法もありかなり文字列操作の中でも奥深い技術を必要とする操作です。まずは単純な検索と置換。それと簡単な正規表現にチャレンジしてみましょう。正規表現の使い方をここでは取り上げて、正規表現による問題解決手法は、ここ以外での説明にしたいと思います。 | 文字列の検索と置換は、メモ帳でCtrl+Fとかで検索するようなものや置換する処理と同じように目的の文字列を探す方法とファイルの検索のようにワイルドカードを使う方法があり、検索して一致した文字列を置き換えるという処理も同じみですが、もうひとつの検索と置換のやり方として、正規表現(Regular Expresion)という手法があります。これはワイルドカードによる指定にさらに文字列の先頭にある特定の文字列があって、さらに特定の文字列にはさまれた部分があって、文字列の最後尾にも特定の文字があったら、その挟まれた文字の先頭グループ1、グループ2、…グループnとして、順次、変換法則にしたがって変換といった複雑な指定が可能になる手法です。正規表現は複雑なこともできるし、単純なこともできる。できるだけ複雑なモノを単純にしてから操作をする手法など、検索と置換には多岐にわたる手法が存在します。また、検索効率をあげる手法もありかなり文字列操作の中でも奥深い技術を必要とする操作です。まずは単純な検索と置換。それと簡単な正規表現にチャレンジしてみましょう。正規表現の使い方をここでは取り上げて、正規表現による問題解決手法は、ここ以外での説明にしたいと思います。 | ||
+ | |||
+ | 以下、ICUのRegexを使った検索と置換の正規表現による処理のサンプルです。 | ||
+ | <syntaxhighlight lang="cpp" line start="1"> | ||
+ | #pragma once | ||
+ | |||
+ | #include "stdafx.h" | ||
+ | #include <stdlib.h> | ||
+ | #include <string> //std::string型定義 | ||
+ | #include <mbstring.h> //mbs***関数 | ||
+ | #include <iostream> //cpp cout etc 一般入出力関数 | ||
+ | #include <locale> //Locale関数 | ||
+ | #include <tchar.h> //TCHAR型+_tcs***関数 | ||
+ | #include "atlstr.h" //CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ | ||
+ | #include "atlbase.h" //同上 | ||
+ | #include "cstringt.h" //CStringTの定義プリプロセッサ | ||
+ | #include <vector> | ||
+ | |||
+ | |||
+ | //_bstr_t型を利用するプリプロセッサ | ||
+ | #include "comutil.h" | ||
+ | #ifdef _DEBUG | ||
+ | #pragma comment(lib, "comsuppwd.lib") | ||
+ | #else | ||
+ | #pragma comment(lib, "comsuppw.lib") | ||
+ | #endif | ||
+ | //_bstr_t | ||
+ | |||
+ | //ICU ucnvプリプロセッサ | ||
+ | #include <unicode/ucnv.h> | ||
+ | #include <unicode/translit.h> | ||
+ | #include <unicode/regex.h> | ||
+ | #ifdef _DEBUG | ||
+ | //#pragma comment(lib, "icudt.lib") | ||
+ | #pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も | ||
+ | #pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ | ||
+ | //#pragma comment(lib, "icuiod.lib") | ||
+ | #else | ||
+ | #pragma comment(lib, "icudt.lib") | ||
+ | #pragma comment(lib, "icuind.lib") | ||
+ | #pragma comment(lib, "icuuc.lib") | ||
+ | #pragma comment(lib, "icuio.lib") | ||
+ | #endif | ||
+ | |||
+ | |||
+ | #include "UnicodeConverter.h" | ||
+ | |||
+ | |||
+ | using namespace std; | ||
+ | int _tmain(int argc, _TCHAR* argv[]) | ||
+ | { | ||
+ | |||
+ | _tsetlocale(LC_ALL, _T("Japanese")); | ||
+ | |||
+ | |||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | // 正規表現分割 | ||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | UParseError error; | ||
+ | UErrorCode status = U_ZERO_ERROR; | ||
+ | int nFindCnt = 0; | ||
+ | |||
+ | UnicodeString regex = L"[,/とやも]";//正規表現検索文字 | ||
+ | RegexPattern* pattern = RegexPattern::compile(regex, error, status);//パターンを登録(コンパイル) | ||
+ | assert( U_SUCCESS(status) ); | ||
+ | |||
+ | UnicodeString input = L"リンゴとミカン/ナシとモモ,ごはんやおかず/おさけもつまみ,programと技術"; | ||
+ | assert( U_SUCCESS(status) ); | ||
+ | |||
+ | while ( matcher->find() ) { | ||
+ | //それぞれのマッチグループの出力。パターンに部分一致(.)が無い場合はつねにグループは1で要素0番のみ | ||
+ | for ( int32_t i = 0; i <= matcher->groupCount(); i++ ) { | ||
+ | std::wcout << L"[" << matcher->start(i,status) | ||
+ | << L"," << matcher->end(i,status) | ||
+ | << L"]" << matcher->group(i,status).getTerminatedBuffer() | ||
+ | << std::endl; | ||
+ | assert( U_SUCCESS(status) ); | ||
+ | } | ||
+ | //whileループ回数をカウントして分割数を記録 | ||
+ | nFindCnt++; | ||
+ | std::wcout << std::endl; | ||
+ | } | ||
+ | |||
+ | //分割した回数のUnicodeString変数の配列を確保して分割結果を配列に格納。 | ||
+ | std::wcout << nFindCnt << std::endl; | ||
+ | UnicodeString* pfruits = new UnicodeString[nFindCnt + 1]; | ||
+ | splits = pattern->split(input, pfruits, nFindCnt + 1, status);//登録されたパターンで分割処理 | ||
+ | //splitの引数には入力文字列,要素が確保された出力文字列配列変数,分割数,実行結果 | ||
+ | |||
+ | assert( U_SUCCESS(status) ); | ||
+ | |||
+ | //分割によりそれぞれに格納された文字を文字列配列の全出力で確認 | ||
+ | for ( int32_t i = 0; i < splits; i++ ) { | ||
+ | std::wcout << pfruits[i].getTerminatedBuffer() << std::endl; | ||
+ | std::wcout << (*(pfruits + i)).getTerminatedBuffer() << std::endl;//こうやって表現しても同じ。 | ||
+ | } | ||
+ | |||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | // 正規表現置換 | ||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | UnicodeString replacement = L"+"; | ||
+ | |||
+ | /* | ||
+ | * 先頭置換 : replaceFirst | ||
+ | */ | ||
+ | result = matcher->replaceFirst(replacement, status); | ||
+ | assert( U_SUCCESS(status) ); | ||
+ | std::wcout << result.getTerminatedBuffer() << std::endl; | ||
+ | |||
+ | /* | ||
+ | * 一括置換 : replaceAll | ||
+ | */ | ||
+ | result = matcher->replaceAll(replacement, status); | ||
+ | assert( U_SUCCESS(status) ); | ||
+ | std::wcout << result.getTerminatedBuffer() << std::endl; | ||
+ | |||
+ | /* | ||
+ | * 追記置換 : appendReplacement/appendAll 前方から逐次置換してresultに追加していく方式 | ||
+ | */ | ||
+ | result = L""; | ||
+ | matcher->reset(); | ||
+ | while ( matcher->find() ) { | ||
+ | matcher->appendReplacement(result, replacement, status); | ||
+ | assert( U_SUCCESS(status) ); | ||
+ | //最後の置換完了状態で一時出力 | ||
+ | std::wcout << result.getTerminatedBuffer() << std::endl; | ||
+ | } | ||
+ | |||
+ | |||
+ | //最後の置換以降の残った文字列を追加 | ||
+ | matcher->appendTail(result); | ||
+ | std::wcout << result.getTerminatedBuffer() << std::endl; | ||
+ | |||
+ | |||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | // 部分一致 | ||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | UnicodeString regex = L"(.)[,/とやも]";//正規表現検索文字 | ||
+ | /* | ||
+ | * 部分マッチ"(.)"の置換 | ||
+ | */ | ||
+ | typedef std::pair<int32_t,int32_t> range_type; | ||
+ | std::vector<range_type> matches; | ||
+ | matcher->reset(); | ||
+ | while ( matcher->find() ) { | ||
+ | // 第一グループを保存する | ||
+ | matches.push_back(range_type(matcher->start(1,status), | ||
+ | matcher->end(1,status))); | ||
+ | assert( U_SUCCESS(status) ); | ||
+ | } | ||
+ | UnicodeString replacement = L"★"; | ||
+ | UnicodeString result = input; | ||
+ | // 末尾から置換 | ||
+ | while ( !matches.empty() ) { | ||
+ | range_type range = matches.back(); | ||
+ | matches.pop_back(); | ||
+ | result.replace(range.first, range.second - range.first, | ||
+ | replacement); | ||
+ | } | ||
+ | std::wcout << result.getTerminatedBuffer() << std::endl; | ||
+ | |||
+ | delete[] pfruits; | ||
+ | delete matcher; | ||
+ | delete pattern; | ||
+ | |||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | 出力結果 | ||
+ | <syntaxhighlight lang="text"> | ||
+ | |||
+ | 0 | ||
+ | [3,4]と | ||
+ | |||
+ | 0 | ||
+ | [7,8]/ | ||
+ | |||
+ | 0 | ||
+ | [10,11]と | ||
+ | |||
+ | 0 | ||
+ | [13,14], | ||
+ | |||
+ | 0 | ||
+ | [17,18]や | ||
+ | |||
+ | 0 | ||
+ | [21,22]/ | ||
+ | |||
+ | 0 | ||
+ | [25,26]も | ||
+ | |||
+ | 0 | ||
+ | [29,30], | ||
+ | |||
+ | 0 | ||
+ | [37,38]と | ||
+ | |||
+ | 9 | ||
+ | リンゴ | ||
+ | リンゴ | ||
+ | ミカン | ||
+ | ミカン | ||
+ | ナシ | ||
+ | ナシ | ||
+ | モモ | ||
+ | モモ | ||
+ | ごはん | ||
+ | ごはん | ||
+ | おかず | ||
+ | おかず | ||
+ | おさけ | ||
+ | おさけ | ||
+ | つまみ | ||
+ | つまみ | ||
+ | program | ||
+ | program | ||
+ | 技術 | ||
+ | 技術 | ||
+ | リンゴ+ミカン/ナシとモモ,ごはんやおかず/おさけもつまみ,programと技術 | ||
+ | リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+つまみ+program+技術 | ||
+ | リンゴ+ | ||
+ | リンゴ+ミカン+ | ||
+ | リンゴ+ミカン+ナシ+ | ||
+ | リンゴ+ミカン+ナシ+モモ+ | ||
+ | リンゴ+ミカン+ナシ+モモ+ごはん+ | ||
+ | リンゴ+ミカン+ナシ+モモ+ごはん+おかず+ | ||
+ | リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+ | ||
+ | リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+つまみ+ | ||
+ | リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+つまみ+program+ | ||
+ | リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+つまみ+program+技術 | ||
+ | |||
+ | ★と★/★と★,★や★/★も★,★と★ | ||
+ | |||
+ | </syntaxhighlight> | ||
=='''文字列ファイルパス操作'''== | =='''文字列ファイルパス操作'''== |