NOEMBLEM/エンブレムが設定されていません。

メールの詳細(トピック表示)

添削願い

投稿者:TDaさん  2002/03/19 23:01  MLNo.132   [メール表示]

みなさんこんにちは、TDaです。

さて本日は添削をお願いしたく投稿しました。
文字列のなかでキーワードを探しそれを別のキーワードに置き換える関数
です。何とか動くコードは書けたのですが釈然としない点がありましてみ
なさまに添削していただきたく投稿いたします。

問題点は一応自分でも把握しておりまして関数の中で確保したバッファを
そのまま新たな文字列として使っているところです。
C FAQでもこのようなことはまずいあくまでも呼び出し側でバッファは確
保するようにとのことなのですが、たとえばgsubの引数で与えられたpstr
に対してreallocするようなことは合法なのでしょうか。

またいまのままでは文字列中にキーワードが2回以上現れたときは2番目以
降は置き換わりません。置き換えた文字列をreturnするようにすれば再帰
呼び出しでうまく処理できそうなのですが上記のようなことを考えるとう
まい書き方を思いつきません。

strcatのソースを当たろうかとおもったらソースがインストールされてい
なくてプロトタイプ宣言しか見つかりませんでした。

それではよろしくお願いします。

//////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void gsub(char **pstr, const char *key1, const char *key2)
{
/*
* key_stringに含まれるkey1をkey2に置換するというプログラム
*/
char *pos = NULL;
char *dest = NULL;
char *dest_start = NULL;
int new_len, i;

/* 新たな文字列の長さを取得 */
new_len = strlen(*pstr) + strlen(key2) - strlen(key1) + 1;

/* key1の先頭のポインタを取得 */
pos = strstr(*pstr, key1);

/* key1に一致しなかったらそのまま関数を抜ける */
if (pos == NULL)
return;

/* メモリの確保とその先頭位置を取得 */
dest_start = dest = malloc(new_len);

/* メモリの確保に失敗したら死ぬ */
if (dest == NULL)
exit(1);

/* posの位置までdestにstrをコピー */
while (*pstr != pos) {
*dest = **pstr;
(*pstr)++;
dest++;
}

/* key1の長さ分strのポインタを進める */
for (i = 0; i < strlen(key1); i++)
(*pstr)++;

/* key2をdestにコピー */
for (i = 0; i < strlen(key2); i++)
*dest++ = *key2++;

/* strの残りをdestにコピー */
while (**pstr != '\0') {
*dest = **pstr;
dest++;
(*pstr)++;
}

/* NUL文字をつける */
dest++;
*dest = '\0';

/* destを新たなstrとする。このままじゃまずいよね。 */
*pstr = dest_start;
}
int main(void)
{
char *key1 = "abc";
char *key2 = "xyzzy";
char *str = "hogehogeabcdef";

printf("str[%s], key1[%s], key2[%s]\n", str, key1, key2);
printf("str[%p], key1[%p], key2[%p]\n", str, key1, key2);
str = gsub(str, key1, key2);
printf("=================================================\n");
printf("[%p] => %s\n", str, str);

return 0;
}

/***************************************/
/* TDa mailto:tda@… */
/***************************************/


このエントリーをはてなブックマークに追加


  • MLNo.133   TDaさん  (0) 2002/03/19 23:18  [メール表示する]
    スイマセン。先ほどのソースですが異なるヴァージョンの関数もあったの
    でそっちを呼び出すサンプルプログラムになっていました。

    誤 str = gsub(str, key1, key2);
    正 gsub(&str, key1, key2);

    > int main(void)
    > {
    > char *key1 = "abc";
    > char *key2 = "xyzzy";
    > char *str = "hogehogeabcdef";
    >
    > printf("str[%s], key1[%s], key2[%s]\n", str, key1, key2);
    > printf("str[%p], key1[%p], key2[%p]\n", str, key1, key2);

    > str = gsub(str, key1, key2);
    > printf("=================================================\n");
    > printf("[%p] => %s\n", str, str);
    >
    > return 0;
    > }



    /***************************************/
    /* TDa mailto:tda@… */
    /***************************************/

  • MLNo.134   Hirokazu Haraさん  (0) 2002/03/20 00:04  [メール表示する]
    原です。

    > 問題点は一応自分でも把握しておりまして関数の中で確保したバッファを
    > そのまま新たな文字列として使っているところです。
    > C FAQでもこのようなことはまずいあくまでも呼び出し側でバッファは確
    > 保するようにとのことなのですが、たとえばgsubの引数で与えられたpstr
    > に対してreallocするようなことは合法なのでしょうか。
    gsub内でreallocするのは呼び出し側が、char *型で宣言されているときだけで
    char name[12];と宣言されていると、コンパイル時に
    incompatible types in assignment
    とエラーになるとおもいます。

    > strcatのソースを当たろうかとおもったらソースがインストールされてい
    > なくてプロトタイプ宣言しか見つかりませんでした。
    こんなことしているかどうか分からないですが
    こんな漢字では。
    char *strcat(char *src, char *dis)
    {
    char *p = src+strlen(src)+1;
    char *r = dis;

    while (r != '\0') {
    p++ = r++;
    }
    *p = '\0';
    return p;
    }
    ## 即席で書いたのでバグがあるとおもいます。。。

    > //////////////////////////////////////////////////////////////////
    >
    > #include <stdio.h>
    > #include <string.h>
    > #include <stdlib.h>
    > void gsub(char **pstr, const char *key1, const char *key2)
    > {
    > /*
    > * key_stringに含まれるkey1をkey2に置換するというプログラム
    > */
    > char *pos = NULL;
    > char *dest = NULL;
    > char *dest_start = NULL;
    > int new_len, i;
    >
    > /* 新たな文字列の長さを取得 */
    > new_len = strlen(*pstr) + strlen(key2) - strlen(key1) + 1;
    >
    > /* key1の先頭のポインタを取得 */
    > pos = strstr(*pstr, key1);
    >
    > /* key1に一致しなかったらそのまま関数を抜ける */
    > if (pos == NULL)
    > return;
    >
    > /* メモリの確保とその先頭位置を取得 */
    > dest_start = dest = malloc(new_len);
    以下のようにした方がいいのでは?
    dest = (char *)malloc(sizeof(char) * new_len);
    dest_start = dest;

    私が個人的につかっている文字列置換関数があるので参考にされては
    (考慮していないこと、バグが存在するかも知れませんが。。。)

    /*
    * 文字列置換関数
    * lenより長くなったらその時点で置換を打ち切る
    *
    * <引数>
    * char *ser [I] 検索文字列
    * char *rep [I] 置換文字列
    * int len [I] 置換対象文字列の長さ
    * char *str [I] 置換対象文字列
    *
    * <戻値>
    * char *str [O] 置換後文字列
    * char * [O] 置換後文字列
    * len の長さより長くなった時は NULL
    *
    */
    char *ustrstr( char *text, int len, char *pattern )
    {
    return strstr( text, pattern );
    }
    char *strrep( char *ser, char *rep, int len, char *str )
    {
    char *p = str;
    char buf[len];
    int serLen = strlen( ser );
    int repLen = strlen( rep );
    int strLen = strlen( str );

    buf[0] = '\0';
    func = ( repLen >= 5 ) ? strstrBM : ustrstr;
    while ( ( p = func( p, len, ser )) != NULL ) {
    if ( len >= (strLen + repLen - serLen) ) {
    snprintf( buf, len, "%s", p+serLen );
    *p = '\0';
    snprintf( str, len, "%s%s%s", str, rep, buf );
    p += repLen;
    strLen = strlen( str );
    } else
    return NULL;
    }
    return str;
    }

    func は関数ポインターで 置換先文字列が 5文字以上の場合
    BM法という検索ルーチンを利用するようにしています。
    BM法に付いてはアルゴリズム辞典等で確認してください。
    # あらためてソース見ると、あからさまにぶさいくですね。。。
    # whileの次のifなんて考えたらもっとスマートにできたよな。。。

  • MLNo.135   なかさん  (0) 2002/03/20 00:10  [メール表示する]
    こんばんは、なか@C勉強中です。

    ちょっと気づいたことです。

    > /* key2をdestにコピー */
    > for (i = 0; i < strlen(key2); i++)
    > *dest++ = *key2++;
    コピーしながらkey2のポインタを移動しているので
    forの条件文が変化してしまうのでは?

    > /* NUL文字をつける */
    > dest++;
    なぜ、ここでdest++をしているかよく分かりません?



  • MLNo.136   TDaさん  (0) 2002/03/21 00:34  [メール表示する]
    なかさんへ

    添削ありがとうございます。

    > > /* key2をdestにコピー */
    > > for (i = 0; i < strlen(key2); i++)
    > > *dest++ = *key2++;
    > コピーしながらkey2のポインタを移動しているので
    > forの条件文が変化してしまうのでは?
    そうですね。上の方でi < strlen(key1)としていたので同じように書いて
    しまいました。最初の方で一時変数を用意して
    key2_len = strlen(key2);
    .............
    for (i = 0; i < key2_len; i++)
    としなくてはいけませんでした。

    > > /* NUL文字をつける */
    > > dest++;
    > なぜ、ここでdest++をしているかよく分かりません?
    これも全くの勘違いです。たぶんK&Rのstrlen関数のソースを見ていてNUL
    文字の分はカウントされないと刷り込まれているからです。

    こんな短いソースなのにつまらないミスが多いです。精進しますので今後
    ともよろしくお願いします。

    /***************************************/
    /* TDa mailto:tda@… */
    /***************************************/

  • MLNo.137   TDaさん  (0) 2002/03/21 00:42  [メール表示する]
    Hirokazu Haraさんへ

    添削ありがとうございます。

    > こんなことしているかどうか分からないですが
    > こんな漢字では。
    > char *strcat(char *src, char *dis)
    > {
    > char *p = src+strlen(src)+1;
    > char *r = dis;
    >
    > while (r != '\0') {
    > p++ = r++;
    > }
    > *p = '\0';
    > return p;
    > }
    > ## 即席で書いたのでバグがあるとおもいます。。。
    >

    p自体は呼び出し側から渡されたstrで初期化しているのでそれを返り値と
    すればよいということでいいでしょうか。

    > 以下のようにした方がいいのでは?
    > dest = (char *)malloc(sizeof(char) * new_len);
    > dest_start = dest;
    これも最後にstrにstrcpy(str, dest_start)すれば合法ですか?もちろん
    strの大きさは十分だとしての話です。

    > 私が個人的につかっている文字列置換関数があるので参考にされては
    > (考慮していないこと、バグが存在するかも知れませんが。。。)
    > func は関数ポインターで 置換先文字列が 5文字以上の場合
    > BM法という検索ルーチンを利用するようにしています。
    > BM法に付いてはアルゴリズム辞典等で確認してください。
    > # あらためてソース見ると、あからさまにぶさいくですね。。。
    > # whileの次のifなんて考えたらもっとスマートにできたよな。。。
    手持ちのアルゴリズム辞典に載っていました。

    ではまたよろしくお願いします。

    /***************************************/
    /* TDa mailto:tda@… */
    /***************************************/


メールへの返信はMLのメンバーしかできません。

更新順メールリスト