C言語関係掲示板

過去ログ

No.1292 2バイト文字を含む文字列で、(パターン)検索・置換

[戻る] [ホームページ]
No.17199

2バイト文字対応の検索
投稿者---Sciggepy(2004/10/09 11:44:47)


2バイト文字を含む文字列で、(パターン)検索・置換を行いたいのですが、1バイト文字の関数のどの部分を変更すればよいのでしょうか?

以下、1バイト文字での実験に使ったソースです。
#include <stdio.h>
#include <stdlib.h>

struct prds {
    const char *fs;
    unsigned len;
} prdata[10];
unsigned nprd;

#define prd_set(f,l,n) {prdata[n].fs=(f); prdata[n].len=(l);}

void prd_insert(const char *f,unsigned l,unsigned n)
{
    unsigned i;
    for(i=n;i<9;i++) prdata[i+1]=prdata[i];
    prd_set(f,l,n);
}

int patfind(const char *str,const char *pat)
{
    const char *fs;
    unsigned long c,i,nd;
    for(;;)
        switch(*pat) {
            case '\0': return *str==*pat;
            case '\\': if(*(str++)!=*(++pat)) return 0; pat++; break; 
            case '?':
                if(!(c=strtoul(++pat,(char **)&pat,10))) c=1;
                for(i=0;i<c;i++,str++) if(*str=='\0') return 0;
                if(nd<10) prd_set(str-i,i,nd);
                nprd++;
                break;
            case '*':
                for(fs=str,pat++,nd=nprd,i=0;*str;str++,i++) if(patfind(str,pat)) {
                    prd_insert(fs,i,nd);
                    nprd++;
                    return 1;
                }
                if(*str==*pat) {
                    prd_insert(fs,i,nd);
                    nprd++;
                    return 1;
                }
                return 0;
            default: if(*(str++)!=*(pat++)) return 0;
        }
}

unsigned patrep(const char *str,const char *pat,const char *opt,char *ret,unsigned cmax)
{
    unsigned i,j,len;
    for(i=0;i<10;i++) prd_set(NULL,0,i);
    nprd=0;
    if(patfind(str,pat)) {
        for(i=nprd;i<10;i++) prd_set(NULL,0,i);
        for(i=0,len=0;opt[i];i++) {
            if(opt[i]=='$') {
                if(opt[++i]=='$') {
                    if(len>=cmax-1) break;
                    ret[len++]='$';
                } else if((opt[i]>='0')||(opt[i]<='9')) {
                    struct prds *pd;
                    pd=&prdata[opt[i]-'0'];
                    for(j=0;j<pd->len;j++) {
                        if(len>=cmax-1) break;
                        ret[len++]=pd->fs[j];
                    }
                }
            } else {
                if(len>=cmax-1) break;
                ret[len++]=opt[i];
            }
        }
        ret[len]='\0';
        return len;
    }
    return -1;
}

int main(void)
{
    char sr[32];
    if(patrep("ab=len(\"bcd\");","?2=*(*);","name: $0, func: $1, arg: $2",sr,32)!=-1)
        printf("%s\n",sr);
    return 0;
}



No.17204

Re:2バイト文字対応の検索
投稿者---西園寺(2004/10/10 08:52:49)


コメントが全然ないもんだから、ぱっと見ただけじゃさっぱり分かりません。。。
他人に見てもらおうというのであれば、せめて
・各関数の概要と引数の説明
・for()ループで何をしているか
くらいは書いて欲しいです。
#最近、コードの読解力が落ちてる気がする。年かなぁ…(;_;)


No.17207

Re:2バイト文字対応の検索
投稿者---Sciggepy(2004/10/10 11:33:00)


すみません。コメントを追加しました。
struct prds {
    const char *fs;    //埋め込むパターン部分の文字列の先頭
    unsigned len;    //長さ
} prdata[10];
unsigned nprd;    //パターン部分のインデックス($nのn)

//prdataに値を設定
#define prd_set(f,l,n) {prdata[n].fs=(f); prdata[n].len=(l);}

//prdataに値を挿入
void prd_insert(const char *f,unsigned l,unsigned n)
{
    unsigned i;
    for(i=n;i<9;i++) prdata[i+1]=prdata[i];
    prd_set(f,l,n);
}

//パターン照合+prdataの設定
//str: 文字列
//pat: パターン
//    ?: 任意の1文字 ?n: 任意のn文字(n=0はn=1と見なす)
//    *: 任意の文字列 \: 直後の文字をただの文字として扱う
int patmatch(const char *str,const char *pat)
{
    const char *fs;
    unsigned long c,i,nd;
    for(;;)
        switch(*pat) {
            case '\0': return *str==*pat;
            case '\\': if(*(str++)!=*(++pat)) return 0; pat++; break; 
            case '?':
                if(!(c=strtoul(++pat,(char **)&pat,10))) c=1;
                for(i=0;i<c;i++,str++) if(*str=='\0') return 0;
                if(nd<10) prd_set(str-i,i,nprd);
                nprd++;
                break;
            case '*':
                //ndに挿入位置を保持して*以降を照合
                for(fs=str,pat++,nd=nprd,i=0;*str;str++,i++) if(patmatch(str,pat)) {
                    prd_insert(fs,i,nd);
                    nprd++;
                    return 1;
                }
                if(*str==*pat) {    //*str=='\0'
                    prd_insert(fs,i,nd);
                    nprd++;
                    return 1;
                }
                return 0;
            default: if(*(str++)!=*(pat++)) return 0;
        }
}

//パターン置換
//opt: 出力形式
//    $n(0=<n=<9): 先頭からn+1番目のパターン部分の文字列
//    $$: $
//ret: 戻り値を格納する領域の先頭
//cmax: 最大文字数('\0'を含む)
//plen: 格納された文字数
int patrep(const char *str,const char *pat,const char *opt,char *ret,unsigned cmax,unsigned *plen)
{
    unsigned i,j,len;
    for(i=0;i<10;i++) prd_set(NULL,0,i);    //prdataを0で埋める
    nprd=0;    //nprdを0に設定
    if(patmatch(str,pat)) {
        for(i=nprd;i<10;i++) prd_set(NULL,0,i);
        for(i=0,len=0;opt[i];i++) {
            if(opt[i]=='$') {
                if(opt[++i]=='$') {
                    if(len>=cmax-1) break;
                    ret[len++]='$';
                } else if((opt[i]>='0')||(opt[i]<='9')) {
                    struct prds *pd;
                    pd=&prdata[opt[i]-'0'];    //prdataの要素を参照
                    for(j=0;j<pd->len;j++) {    //文字列を追加
                        if(len>=cmax-1) break;
                        ret[len++]=pd->fs[j];
                    }
                }
            } else {
                if(len>=cmax-1) break;
                ret[len++]=opt[i];
            }
        }
        ret[len]='\0';
        if(plen) *plen=len;
        return 1;    //パターンにマッチした
    }
    return 0;    //パターンにマッチしなかった
}



No.17205

Re:2バイト文字対応の検索
投稿者---もぐりん(2004/10/10 10:26:57)


開発環境は?
ぱっと見ただけで気づいたことを書きます。

・unsignedしか書いていない変数は、int型?
・unsignedなのに戻り値が-1なのはなぜ?
・変数ndは値を代入(初期化)していないのに、30行目のif文で
 参照している

結構無茶な書き方してませんか?






No.17206

Re:2バイト文字対応の検索
投稿者---Sciggepy(2004/10/10 11:05:41)


>・unsignedしか書いていない変数は、int型?
そうです。

>・unsignedなのに戻り値が-1なのはなぜ?
0xffffffffのつもりですが、signedとunsignedの比較は変ですね。戻り値は、パターンにマッチしたかどうかを表す真偽値に変えます。

>・変数ndは値を代入(初期化)していないのに、30行目のif文で
> 参照している
これはnprdの間違いです。

>結構無茶な書き方してませんか?
<(~~;



No.17210

Re:2バイト文字対応の検索
投稿者---かずま(2004/10/10 16:30:42)


本当は全部書き換えたいけど、最小限の修正に留めておきました。
まだ怪しい部分がありますが、これでいかがでしょうか?
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

struct prds {
    const char *fs;
    unsigned len;
} prdata[10];

unsigned nprd;

#define prd_set(f, l, n) { prdata[n].fs = (f); prdata[n].len = (l); }

void prd_insert(const char *f, unsigned l, unsigned n)
{
    unsigned i;
    for (i = 9; i > n; i--) prdata[i] = prdata[i-1];
    prd_set(f, l, n);
}

int patfind(const char *str, const char *pat)
{
    const char *fs;
    unsigned long c, i, nd;
    for (;;)
        switch (*pat) {
        case '\0':
            return *str == *pat;
        case '\\':
            c = mblen(++pat, MB_CUR_MAX);
            for (i = 0; i < c; i++)
                if (*str++ != *pat++) return 0;
            break; 
        case '?':
            c = strtoul(++pat, (char **)&pat, 10);
            if (c == 0) c = 1;
            fs = str;
            for (i = 0; i < c; i++, str += mblen(str, MB_CUR_MAX))
                if (*str=='\0') return 0;
            if (nprd < 10) prd_set(fs, str - fs, nprd);
            nprd++;
            break;
        case '*':
            fs = str, pat++, nd = nprd;
            for (i = 0; *str; str += mblen(str, MB_CUR_MAX), i++)
                if (patfind(str, pat)) {
                    prd_insert(fs, str - fs, nd);
                    nprd++;
                    return 1;
                }
            c = mblen(pat, MB_CUR_MAX);
            for (i = 0; i < c; i++)
                if (*str++ != *pat++) return 0;
            prd_insert(fs, str - fs, nd);
            nprd++;
            return 1;
        default:
            c = mblen(pat, MB_CUR_MAX);
            for (i = 0; i < c; i++)
                if (*str++ != *pat++) return 0;
        }
}

int patrep(const char *str, const char *pat, const char *opt,
        char *ret, unsigned cmax)
{
    unsigned i, j, len;

    for (i = 0; i < 10; i++) prd_set(NULL, 0, i);
    nprd = 0;
    if (patfind(str, pat)) {
        for (i = nprd; i < 10; i++) prd_set(NULL, 0, i);
        for (i = len = 0; opt[i]; i++) {
            if (opt[i] == '$') {
                if (opt[++i] == '$') {
                    if (len >= cmax-1) break;
                    ret[len++] = '$';
                } else if (opt[i] >= '0' || opt[i] <= '9') {
                    struct prds *pd = &prdata[opt[i]-'0'];
                    for (j = 0; j < pd->len; j++) {
                        if (len >= cmax-1) break;
                        ret[len++] = pd->fs[j];
                    }
                }
            } else {
                if (len >= cmax-1) break;
                ret[len++] = opt[i];
            }
        }
        ret[len] = '\0';
        return len;
    }
    return -1;
}

int main(void)
{
    char sr[256];

    setlocale(LC_CTYPE, "");
    if (patrep("あい=うえお(\"かきく\");", "?2=*(*);",
                "name: $0, func: $1, arg: $2", sr, sizeof sr) != -1)
        printf("%s\n", sr);
    return 0;
}



No.17224

Re:2バイト文字対応の検索
投稿者---Sciggepy(2004/10/11 13:13:19)


返信ありがとうございます。
マルチバイト文字も、1文字として数えるほうが便利ですね。

>まだ怪しい部分がありますが、これでいかがでしょうか?
やはり、問題になりそうな部分がありますか?



No.17231

Re:2バイト文字対応の検索
投稿者---かずま(2004/10/12 11:48:39)


>> マルチバイト文字も、1文字として数えるほうが便利ですね。

?3 が 3文字でなく 3バイトだとしたら、困りませんか?


>> まだ怪しい部分がありますが、これでいかがでしょうか?
>> やはり、問題になりそうな部分がありますか?
patrep("abcdef", "**", ":$0:$1:", sr, sizeof sr)
$0 = ""
$1 = "abcdef"

patrep("ab-cd-ef", "*-*", ":$0:$1:", sr, sizeof sr)
$0 = "ab"
$1 = "cd-ef"

こういう仕様ということでよいのでしょうか?

* が最長一致なら、たとえば後者は $0 = "ab-cd", $1 = "ef" となるはずです。


No.17245

Re:2バイト文字対応の検索
投稿者---Sciggepy(2004/10/12 16:47:48)


patrep("abcdef", "**", ":$0:$1:", sr, sizeof sr)
$0 = ""
$1 = "abcdef"

patrep("ab-cd-ef", "*-*", ":$0:$1:", sr, sizeof sr)
$0 = "ab"
$1 = "cd-ef"

>こういう仕様ということでよいのでしょうか?
>
>* が最長一致なら、たとえば後者は $0 = "ab-cd", $1 = "ef" となるはずです。
最長一致、最短一致のどちらにしても、必ずしも上手くはいかないので、最短一致のままでよいことにします。

ありがとうございました。