Index

HOME > プログラムTOP > C++



C++の壺・検索置換篇

 C++の基本テクニックのTipsです。C言語の文字列処理は慣れると非常に便利です。検索置換篇では、標準関数を利用した、文字列の検索と置換の方法を扱います。
[INDEX] 検索とstrstr ... strstr関数を利用した文字列の検索
置換とmemmove ... memmove関数を利用した文字列の置換
すべて置換 ...  文字列の全置換を行うコードのサンプル

sasaraan programming

Exposition

●検索とstrstr

 文字列の検索は、strstr 関数を利用すると簡単に結果を得ることができます。この関数は、対象文字列の先頭から検索文字列を探し、見つかればその先頭位置へのポインタを、見つからなければ NULL を返します。検索には、終端の NULL 文字は含まれません。
strstr function
・書式char *strstr(const char *src, const char *find);
・機能文字列の検索
・引数src...対象文字列、find...検索文字列
・戻り値見つかった場所へのポインタ(見つからない時は NULL)
・要件string.h のインクルード
 上記中、引数の src と find は、いずれも NULL で終わる文字列である必要があります。
// 文字列の検索
#include <stdio.h>     // printf
#include <conio.h>    // getch
#include <string.h>    // strstr

// main 関数
void main() 
{
    char src[256] = "They Will Leave for Kyoto on Sunday";    // 対象文字列
    char *find = "Kyoto";         // 検索文字列
    char *res;                         // 結果文字列

    res = strstr(src, find);        // 検索の実行
    if (res == NULL) {
        printf("Not Found\n");
    } else {
        printf("%s\n", res);         // "Kyoto on Sunday"
    }

    if (getch()) return;
} 

●置換とmemmove

 置換は、まず [1]検索 を行い、見つかったら、以降の文字列を必要に応じて [2]移動 し、検索文字列に置換文字列を [3]上書き するという手順になります。[2]の段階で、検索文字列の長さと、置換文字列の長さが異なる場合は、置換場所以降の文字列の移動(シフト)が必要です。

*** 置換A : 文字列が増えるケース ("Kyoto" を "Fukuoka" に置換) ***
[1] They Will Leave for Kyoto on Sunday.  (Kyoto を 検索)
[2] They Will Leave for KyKyoto on Sunday.(Fukuoka(7)-Kyoto(5)= 2 文字分シフト)
[3] They Will Leave for Fukuoka on Sunday.(Fukuoka を KyKyoto に上書き)

*** 置換B : 文字列が減るケース ("Fukuoka" を "Kyoto" に置換) ***
[1] They Will Leave for Fukuoka on Sunday.(Fukuoka を 検索)
[2] They Will Leave for kuoka on Sunday. (Kyoto(5)-Fukuoka(7)= -2 文字分シフト)
[3] They Will Leave for Kyoto on Sunday. (Kyoto を kuoka に上書き)

 文字列のシフトには、バッファ(メモリ領域)間でのコピー機能を持つ memmove 関数が利用できます。この関数は、コピー先とコピー元が重なった時でも上書きしてコピーします。そのため、文字列の移動(シフト)を容易に実現することができます。
memmove function
・書式void *memmove(void *dest, const void *src, size_t n);
・機能バッファ間の上書きコピー
・引数dest...コピー先バッファ、src...コピー元バッファ、n...文字(バイト)数
・戻り値コピー先バッファへのポインタ
・要件string.h のインクルード
 下例で、strlen 関数を使用する際、末端の NULL は含みません。一方、文字列を移動させる際は、末端の NULL 文字も移動させる必要があるので、+1 文字分を加えます。逆に、置換の際は NULL 文字までコピーすると、そこで文字列が終わってしまいますので、strlen の値をそのまま使用します。
// 文字列の置換
#include <stdio.h>     // printf
#include <conio.h>    // getch
#include <string.h>    // strstr, memmove

// main 関数
void main() 
{
    char src[256] = "They Will Leave for Kyoto on Sunday";    // 対象文字列
    char *find = "Kyoto";                            // 検索文字列
    char *rep = "Fukuoka";                         // 置換文字列
    int shift =(int)(strlen(rep) - strlen(find));  // = 2 : 文字数の差(シフトさせる値)

    // [1]検索実行 
    char *res;                                       // 結果文字列
    res = strstr(src, find);                      // "Kyoto on Sunday"

    if (res != NULL) 
    {
        // [2]文字列のシフト
        if (shift > 0) {
            memmove(res+shift, res, strlen(res)+1);          // 文字列を広げる
        } else if (shift < 0) {
            memmove(res, res-shift, strlen(res)+shift+1);  // 文字列を詰める
        }
 
        // [3]置換の実行
        memmove(res, rep, strlen(rep)); 
    }

    printf("%s\n", src);                // "They Will Leave for Fukuoka on Sunday"
    if (getch()) return;
} 
 上記のサンプルでは省いてありますが、実際には、置換後の文字列長がオーバーしないように気を配ることも必要となります。

●すべて置換

 文字列内の特定の文字列すべてを一度に置換するには、[1]検索、[2]移動(シフト)、[3]置換 を、検索結果 が NULL になるまでくりかえします。以下にその例を挙げておきます。なお、ここでは置換後の文字列長のチェックは行っていません。
// すべて置換する例
#include <stdio.h>     // printf
#include <conio.h>    // getch
#include <string.h>    // strstr, memmove

// 対象文字列 ('\' は行の連結記号 / 下線は検索文字列)
char src[1024] = "ぼろぼろの身なりにやせ馬。五十石の分際で二人の郎党。"\
"そんな伊右衛門のもとに嫁が来ることに。もと浅井家家来・若宮家の一人娘・千代。"\
"いかにも頼りなげな伊右衛門に対し、千代は美人で賢い上につつましやか。"\
"この時千代は、律儀だけが取柄の伊右衛門を一国一城の主にしようと決意します。";

// main 関数
void main() 
{
    char *find = "伊右衛門";            // 検索文字列
    char *rep = "一豊";                   // 置換文字列
    size_t find_len = strlen(find);        // 検索文字列の長さ = 8
    size_t rep_len = strlen(rep);         // 置換文字列の長さ = 4
    int shift = (int)(rep_len - find_len);  // 移動文字(バイト)数 = -4
    char *start = src;                     // 検索開始位置へのポインタ
    char *hit;                                // 見つかった位置へのポインタ

    // 検索結果が NULL になるまでループ
    while ((hit = strstr(start, find)) != NULL)  /* [1]検索 */
    {
        // [2]シフト
        if (shift > 0) {
            memmove(hit+shift, hit, strlen(hit)+1);          // 拡張
        } else if (shift < 0) { 
            memmove(hit, hit-shift, strlen(hit)+shift+1);  // 切詰
        }
        start = hit + rep_len;              // 次の検索開始位置
        
        // [3]置換
        memmove(hit, rep, rep_len);
    }

    printf("%s\n", src);    // 出力(下記参照 / 下線は置換文字列)
    if (getch()) return;
} 

ぼろぼろの身なりにやせ馬。五十石の分際で二人の郎党。 そんな一豊のもとに嫁が来ることに。もと浅井家家来・若宮家の一人娘・千代。 いかにも頼りなげな一豊に対し、千代は美人で賢い上につつましやか。 この時千代は、律儀だけが取柄の一豊を一国一城の主にしようと決意します。

www.sasaraan.net

(c) morijoh