ワイルドカードを含んだ文字列の削除をしたい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
suumiry

ワイルドカードを含んだ文字列の削除をしたい

#1

投稿記事 by suumiry » 12年前

コード:

//?=1文字strstr
char *wild_strstr(char *str1,char *str2){
       int i,j,k;
          if(*str2 == '\0')
          return str1;
          else{
           for (i = 0; *(str1+i) != '\0'; i++)
         {
           if(*(str1+i) == *str2 || *str2 == '?')
         {
             for (j = i,k = 0;*(str1+j) == *(str2+k)|| *(str2+k) == '?'&& *(str1+j) != ' '; j++,k++);
             if(*(str2+k) == '\0') return str1 + i;
         }
         }
                 return NULL;
     }
}

int WordDel_2(char *setStr,char *inStr,char *outStr){
    int inlen,setlen,ni=0,e=0,p=0,supe[MAX],o=0,shiftlen,ex=0,op=0,qei,del=0;
      inlen=strlen(inStr);
      strcpy(outStr,setStr);
      setlen=strlen(setStr);
//空白部分の判定
        for (int i = 0; i < setlen; i++)
    {
            ni = wild_strstr(&setStr[i]," ")-setStr+1;
            if(p==ni)
    {
    }else{
                supe[o]=ni; 
                o++;
                p=ni;
    }
    }

                supe[o]=0;//先頭
                supe[o+1]=setlen;
                qsort(supe,o+2,sizeof(supe[0]),kouzyun);//並び替え
        for (int i = 1; i < o+1; i++)
        {
           qei=supe[i];
              if(wild_strstr(&setStr[qei],inStr) != NULL)
        {
                  ni = wild_strstr(&setStr[qei],inStr)-setStr+1;
                     qei=supe[i-1];
                     if(ni=='\0'||qei>ni)
        {
                        del++;
        }
    }
}
//指定文字の削除
       while(outStr=wild_strstr(outStr,inStr))
    {
          shiftlen=strlen(outStr+inlen);
              memcpy(outStr,outStr+inlen,shiftlen);
                 *(outStr+shiftlen)=0;
    }
                     return del;
}


現在、学校の課題でワイルドカードを使ったプログラムの作成に取り組んでいます。
下記の仕様を受け、上記プログラムを作ってみたのですが文字列の一番最後に指定した文字列があった場合、その部分の削除および削除した箇所のカウントがされません。


① 入力する文字は半角英字のみ
② 複数の文字列を空白をはさみ1つの文字列とする。
例) ①AB ②CD ③EF →AB CD EF
③ ①で作った文字列から?を1文字としたワイルドカードを含む文字列を全て削除する。ただし、空白は削除しない。
④ 関数値は削除した箇所を返す。
例) 元の文字列→AB CDED EF  削除する文字列→?D  返す関数値→1

suumiry

Re: ワイルドカードを含んだ文字列の削除をしたい

#2

投稿記事 by suumiry » 12年前

すみません、コードの見た目を直していたら途中送信になりました。
聞きたいのは、何故最後に対象の文字があると判別されないのかです。
string関数のstrstr関数を使ってワイルドカードを含まない文字列なら正常に動作したのでwild_strstrに問題があるのでしょうか?

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: ワイルドカードを含んだ文字列の削除をしたい

#3

投稿記事 by h2so5 » 12年前

ソースコードが汚すぎて読めないので投稿する前にもう少し整理してください。インデントは直しておきました。
あと、main関数とMAXの定義がないので完全なコードを書いてください。
suumiry さんが書きました: ① 入力する文字は半角英字のみ
② 複数の文字列を空白をはさみ1つの文字列とする。
例) ①AB ②CD ③EF →AB CD EF
③ ①で作った文字列から?を1文字としたワイルドカードを含む文字列を全て削除する。ただし、空白は削除しない。
④ 関数値は削除した箇所を返す。
例) 元の文字列→AB CDED EF  削除する文字列→?D  返す関数値→1
なぜこの入力で返す関数値が1になるのかが分かりません。
詳しく説明してください。
suumiry さんが書きました: 聞きたいのは、何故最後に対象の文字があると判別されないのかです。
string関数のstrstr関数を使ってワイルドカードを含まない文字列なら正常に動作したのでwild_strstrに問題があるのでしょうか?
(1) 「最後に対象の文字がある」というのは具体的にどのような状況か
(2) その状況でどのような結果が本来は期待されるのか
(3) 実際にはどのような結果になるのか

を明確にしてください。

コード:

//?=1文字strstr
char *wild_strstr(char *str1,char *str2) {
    int i,j,k;
    if(*str2 == '\0')
        return str1;
    else {
        for (i = 0; *(str1+i) != '\0'; i++) {
            if(*(str1+i) == *str2 || *str2 == '?') {
                for (j = i,k = 0;*(str1+j) == *(str2+k)|| *(str2+k) == '?'&& *(str1+j) != ' '; j++,k++);
                if(*(str2+k) == '\0') return str1 + i;
            }
        }
        return NULL;
    }
}

int WordDel_2(char *setStr,char *inStr,char *outStr) {
    int inlen,setlen,ni=0,e=0,p=0,supe[MAX],o=0,shiftlen,ex=0,op=0,qei,del=0;
    inlen=strlen(inStr);
    strcpy(outStr,setStr);
    setlen=strlen(setStr);
    //空白部分の判定
    for (int i = 0; i < setlen; i++) {
        ni = wild_strstr(&setStr[i]," ")-setStr+1;
        if(p==ni) {
        } 
        else {
            supe[o]=ni;
            o++;
            p=ni;
        }
    }
        
    supe[o]=0;//先頭
    supe[o+1]=setlen;
    qsort(supe,o+2,sizeof(supe[0]),kouzyun);//並び替え
    for (int i = 1; i < o+1; i++) {
        qei=supe[i];
        if(wild_strstr(&setStr[qei],inStr) != NULL) {
            ni = wild_strstr(&setStr[qei],inStr)-setStr+1;
            qei=supe[i-1];
            if(ni=='\0'||qei>ni) {
                del++;
            }
        }
    }
    //指定文字の削除
    while(outStr=wild_strstr(outStr,inStr)) {
        shiftlen=strlen(outStr+inlen);
        memcpy(outStr,outStr+inlen,shiftlen);
        *(outStr+shiftlen)=0;
    }
    return del;
}

suumiry

Re: ワイルドカードを含んだ文字列の削除をしたい

#4

投稿記事 by suumiry » 12年前

h2so5 さんが書きました:ソースコードが汚すぎて読めないので投稿する前にもう少し整理してください。インデントは直しておきました。
あと、main関数とMAXの定義がないので完全なコードを書いてください。
すみません。ありがとうございます。
完全なコードは下記の通りです。

コード:

#define MAX 257
//?=1文字strstr
char *wild_strstr(char *str1,char *str2) {
    int i,j,k;
    if(*str2 == '\0')
        return str1;
    else {
        for (i = 0; *(str1+i) != '\0'; i++) {
            if(*(str1+i) == *str2 || *str2 == '?') {
                for (j = i,k = 0;*(str1+j) == *(str2+k)|| *(str2+k) == '?'&& *(str1+j) != ' '; j++,k++);
                if(*(str2+k) == '\0') return str1 + i;
            }
        }
        return NULL;
    }
}

int WordDel_2(char *setStr,char *inStr,char *outStr) {
    int inlen,setlen,ni=0,e=0,p=0,supe[MAX],o=0,shiftlen,ex=0,op=0,qei,del=0;
    inlen=strlen(inStr);
    strcpy(outStr,setStr);
    setlen=strlen(setStr);
    //空白部分の判定
    for (int i = 0; i < setlen; i++) {
        ni = wild_strstr(&setStr[i]," ")-setStr+1;
        if(p==ni) {
        } 
        else {
            supe[o]=ni;
            o++;
            p=ni;
        }
    }
        
    supe[o]=0;//先頭
    supe[o+1]=setlen;
    qsort(supe,o+2,sizeof(supe[0]),kouzyun);//並び替え
    for (int i = 1; i < o+1; i++) {
        qei=supe[i];
        if(wild_strstr(&setStr[qei],inStr) != NULL) {
            ni = wild_strstr(&setStr[qei],inStr)-setStr+1;
            qei=supe[i-1];
            if(ni=='\0'||qei>ni) {
                del++;
            }
        }
    }
    //指定文字の削除
    while(outStr=wild_strstr(outStr,inStr)) {
        shiftlen=strlen(outStr+inlen);
        memcpy(outStr,outStr+inlen,shiftlen);
        *(outStr+shiftlen)=0;
    }
    return del;
}

int main(void){
   int n,e;
   char instr[MAX],setstr[MAX],outstr[MAX],o[MAX];
     printf("いくつの文字列を入力しますか?\n");
     scanf("%d",&n);
     printf("1番目の文字列を入力\n");
     scanf("%s",instr);
     strcpy(setstr,instr);
        for (int i = 1; i < n; i++){
           printf("%d番目の文字列を入力\n",i+1);
           scanf("%s",instr);
           strcat(setstr," ");
           strcat(setstr,instr);
         }
             printf("どの文字を削除しますか?(?を任意の文字、1文字とする)\n");
             scanf("%s",instr);
             n=WordDel_2(setstr,instr,outstr);
             e=strlen(outstr);
             printf("%d箇所削除しました\n",n);
                for (int i = 0; i < e; i++){
                  putchar(outstr[i]);
     }
 }
h2so5 さんが書きました: なぜこの入力で返す関数値が1になるのかが分かりません。
詳しく説明してください。
説明が足らず申し訳ありません。
1つの文字列としていますが、空白をはさんで1つにする前に入力した文字列を1か所として数えています。
なので'AB CDED EF'の場合だと真ん中のブロックのみ削除するので1となります。
'AB CDED ED'だと削除したブロックが2か所なので2になります。
h2so5 さんが書きました: (1) 「最後に対象の文字がある」というのは具体的にどのような状況か
(2) その状況でどのような結果が本来は期待されるのか
(3) 実際にはどのような結果になるのか

を明確にしてください。
①'ABCDEF ABF'から'?F'を削除する場合など、削除対象が一番後ろにある場合です。
②'ABCDEF ABF'から'EF'、'BF'が削除され、'ABCD A'となり関数値が2となることが望ましいです。
③'ABCDEF ABF'から'BF'が消えず、結果が'ABCD ABF'となり、関数値が1になります。

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: ワイルドカードを含んだ文字列の削除をしたい

#5

投稿記事 by みけCAT » 12年前

必要なincludeを補ってコンパイルしましたが、コンパイルエラーが出ました。kouzyun関数が無いようです。

コード:

D:\(略)\wildcard_del>gcc -g3 -std=c99 -o wildcard_del wildcard_del.c
wildcard_del.c: In function 'WordDel_2':
wildcard_del.c:41:33: error: 'kouzyun' undeclared (first use in this function)
wildcard_del.c:41:33: note: each undeclared identifier is reported only once for
 each function it appears in
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

suumiry

Re: ワイルドカードを含んだ文字列の削除をしたい

#6

投稿記事 by suumiry » 12年前

みけCAT さんが書きました:必要なincludeを補ってコンパイルしましたが、コンパイルエラーが出ました。kouzyun関数が無いようです。

コード:

D:\(略)\wildcard_del>gcc -g3 -std=c99 -o wildcard_del wildcard_del.c
wildcard_del.c: In function 'WordDel_2':
wildcard_del.c:41:33: error: 'kouzyun' undeclared (first use in this function)
wildcard_del.c:41:33: note: each undeclared identifier is reported only once for
 each function it appears in
すみません、書き忘れです。

コード:

int kouzyun(const void *a,const void *b){
return *(int*)b-*(int*)a;
}
を追加してもらえますか?

suumiry

Re: ワイルドカードを含んだ文字列の削除をしたい

#7

投稿記事 by suumiry » 12年前

何度もすみません。
インクルードファイルも忘れていました。

コード:

#include<stdib.h>
#include<stdio.h>
#include<string.h>
の3つが必要です。

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: ワイルドカードを含んだ文字列の削除をしたい

#8

投稿記事 by みけCAT » 12年前

No: 4のコードの25行目と414行目で+1するのをやめるか、36行目をsupe[o+1]=setlen+1;とするといいかもしれません。
また、51行目でmemcpyを使用すると変なことが起きるかもしれないので、
課題の条件などの制約がなければmemmoveを使用することをお勧めします。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

suumiry

Re: ワイルドカードを含んだ文字列の削除をしたい

#9

投稿記事 by suumiry » 12年前

みけCAT さんが書きました:No: 4のコードの25行目と414行目で+1するのをやめるか、36行目をsupe[o+1]=setlen+1;とするといいかもしれません。
また、51行目でmemcpyを使用すると変なことが起きるかもしれないので、
課題の条件などの制約がなければmemmoveを使用することをお勧めします。
25、41行目の修正、36行目の修正をしましたが結果は変わりませんでした。
memmoveのほうは特に制約などはないので、使わせて頂きます。

25行目:  ni = wild_strstr(&setStr," ")-setStr+1;     →  ni = wild_strstr(&setStr," ")-setStr;
36行目:  supe[o+1]=setlen;                   →  supe[o+1]=setlen+1;
41行目:  ni = wild_strstr(&setStr[qei],inStr)-setStr+1; →  ni = wild_strstr(&setStr[qei],inStr)-setStr;
51行目:  memcpy(outStr,outStr+inlen,shiftlen);     →  memmove(outStr,outStr+inlen,shiftlen);

36行目:supe[o+1]=setlen; を supe[o+1]=setlen+setlen; にし、入力する文字列を各3文字の空白を入れて合計11文字にするときちんと動作しました。
が、長い文字列だとやはり上手くいきません。

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: ワイルドカードを含んだ文字列の削除をしたい

#10

投稿記事 by みけCAT » 12年前

言い方が悪かったですね。
「25、41行目の修正」もしくは「36行目の修正」のどちらか一方のみを適用してください。

しかし、「25、41行目の修正」のみを適用した状態でも正常動作しませんでした。
入力は1→F→Fです。
デバッガを噛ませるとうまくいくのですが、普通に走らせるとうまくいかないという謎の状態です。
現在調査しています。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: ワイルドカードを含んだ文字列の削除をしたい

#11

投稿記事 by みけCAT » 12年前

文字列の最後はstr1の最後にも'\0'がついているので、一致判定で通過してしまうようです。
No: 4の10行目の真ん中の条件式を

コード:

*(str2+k)!='\0' && (*(str1+j) == *(str2+k)|| *(str2+k) == '?'&& *(str1+j) != ' ')
としてみてください。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

suumiry

Re: ワイルドカードを含んだ文字列の削除をしたい

#12

投稿記事 by suumiry » 12年前

みけCAT さんが書きました:文字列の最後はstr1の最後にも'\0'がついているので、一致判定で通過してしまうようです。
No: 4の10行目の真ん中の条件式を

コード:

*(str2+k)!='\0' && (*(str1+j) == *(str2+k)|| *(str2+k) == '?'&& *(str1+j) != ' ')
としてみてください。
なるほど、最後の\0のせいでforを抜けなったんですね。
そのように修正したらきちんと動作するようになりましたのでこれで解決とさせて頂きます。

h2so5さん
ソースのインデントの修正などの不手際のご指摘ありがとうございました。
みけCATさん
関数の指摘や問題個所の修正などありがとうございました。

以下修正したコードになります。

コード:

#define MAX 257
#include<stdib.h>
#include<stdio.h>
#include<string.h>

int kouzyun(const void *a,const void *b){
return *(int*)b-*(int*)a;
}

//?=1文字strstr
char *wild_strstr(char *str1,char *str2) {
    int i,j,k;
    if(*str2 == '\0')
        return str1;
    else {
        for (i = 0; *(str1+i) != '\0'; i++) {
            if(*(str1+i) == *str2 || *str2 == '?') {
                for (j = i,k = 0;*(str2+k)!='\0' && (*(str1+j) == *(str2+k)|| *(str2+k) == '?'&& *(str1+j) != ' '; j++,k++);
                if(*(str2+k) == '\0') return str1 + i;
            }
        }
        return NULL;
    }
}

int WordDel_2(char *setStr,char *inStr,char *outStr) {
    int inlen,setlen,ni=0,e=0,p=0,supe[MAX],o=0,shiftlen,ex=0,op=0,qei,del=0;
    inlen=strlen(inStr);
    strcpy(outStr,setStr);
    setlen=strlen(setStr);
    //空白部分の判定
    for (int i = 0; i < setlen; i++) {
        ni = wild_strstr(&setStr[i]," ")-setStr;
        if(p==ni) {
        } 
        else {
            supe[o]=ni;
            o++;
            p=ni;
        }
    }
        
    supe[o]=0;//先頭
    supe[o+1]=setlen;
    qsort(supe,o+2,sizeof(supe[0]),kouzyun);//並び替え
    for (int i = 1; i < o+1; i++) {
        qei=supe[i];
        if(wild_strstr(&setStr[qei],inStr) != NULL) {
            ni = wild_strstr(&setStr[qei],inStr)-setStr;
            qei=supe[i-1];
            if(ni=='\0'||qei>ni) {
                del++;
            }
        }
    }
    //指定文字の削除
    while(outStr=wild_strstr(outStr,inStr)) {
        shiftlen=strlen(outStr+inlen);
        memmove(outStr,outStr+inlen,shiftlen);
        *(outStr+shiftlen)=0;
    }
    return del;
}

int main(void){
   int n,e;
   char instr[MAX],setstr[MAX],outstr[MAX],o[MAX];
     printf("いくつの文字列を入力しますか?\n");
     scanf("%d",&n);
     printf("1番目の文字列を入力\n");
     scanf("%s",instr);
     strcpy(setstr,instr);
        for (int i = 1; i < n; i++){
           printf("%d番目の文字列を入力\n",i+1);
           scanf("%s",instr);
           strcat(setstr," ");
           strcat(setstr,instr);
         }
             printf("どの文字を削除しますか?(?を任意の文字、1文字とする)\n");
             scanf("%s",instr);
             n=WordDel_2(setstr,instr,outstr);
             e=strlen(outstr);
             printf("%d箇所削除しました\n",n);
                for (int i = 0; i < e; i++){
                  putchar(outstr[i]);
     }
 }

かずま

Re: ワイルドカードを含んだ文字列の削除をしたい

#13

投稿記事 by かずま » 12年前

suumiry さんが書きました: 以下修正したコードになります。
そのコードはコンパイルできません。
2行目は、<stdib.h> ではなく <stdlib.h> ですね。
18行目は、括弧の対応が取れていません。
86行目は、全角スペースがあります。
33行目は、空白がない時、NULL が返され、そこから setStr を引いてしまいます。

解決になっているようなので、もう見てもらえないかもしれませんが、別解を書いてみました。

コード:

#define MAX 257

#include <stdio.h>   // printf, scanf
#include <string.h>  // strlen

int match(const char *s1, const char *s2)
{
    for (; *s2; s1++, s2++)
        if (*s2 == '?' ? *s1 == 0 || *s1 == ' ' : *s1 != *s2) return 0;
	return 1;
}

int WordDel_2(const char *s1, const char *s2, char *s3)
{
    int del = 0, len = strlen(s2);
    while (*s1)
		match(s1, s2) ? (s1 += len, del++) : (*s3++ = *s1++);
    *s3 = 0;
    return del;
}

int main(void)
{
    int i, n;
    char instr[MAX], setstr[MAX], outstr[MAX], *p = setstr;
    printf("いくつの文字列を入力しますか?\n");
    scanf("%d", &n);
	for (i = 1; i <= n; i++) {
		printf("%d番目の文字列を入力\n", i);
		scanf("%s", p);
        p += strlen(p);
        *p++ = ' ';
    }
    *--p = 0;
    printf("どの文字を削除しますか?(?を任意の文字、1文字とする)\n");
    scanf("%s", instr);
    n = WordDel_2(setstr, instr, outstr);
    printf("%d箇所削除しました\n%s\n", n, outstr);
	return 0;
}

閉鎖

“C言語何でも質問掲示板” へ戻る