C言語関係掲示板

過去ログ

No.371.文字列置換の上手な方法

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

文字列置換の上手な方法
投稿者---yukki(2002/08/22 16:54:58)


あるフォーマット文字列を置換し、構造体に設定する、というロジックを考えています。

具体的には、
AAA="aaa",BBB="bbb",YY="1999",MM="08",DD="12"の場合に、
"AAA,BBB,YY/MM/DD"という文字列をaaa,bbb,1999/08/12と置換して、それぞれ構造体の要素0〜2に設定したいのです。

今のところ考えているロジックを日本語で書いてみました。

1.strchr()を使用して","を検索する
2.検索を開始した先頭のポインタからstrchr()で返却されたポインタまでの
文字列をバッファにコピーする
3.2で取得した値(例:AAA)に対応する値を構造体に設定する
4.1〜3の繰り返し

こうしてしまうと、"YY/MM/DD"の部分がうまく置換できないのです。
"AAA,BBB,YY/MM/DD"のフォーマットをうまく変更すればできるかと思うのですが、思いつきません。

よい方法があったら教えて下さい。
よろしくお願いします。


No.2468

Re:文字列置換の上手な方法
投稿者---たか(2002/08/22 20:02:01)


とても簡単な方法で置換してみました。この方法ですと、置換後の文字列
に置換前の文字列が含まれてしまうとうまくいかないので、どうすれば
いいか考えてみてください。

#include <stdio.h>
#include <string.h>

void change(char *buf, const char *label, const char *string);

struct assoc {
  char *label;
  char *string;
} Assoc[] = {
  {"AAA", "aaa"},
  {"BBB", "bbb"},
  {"YY", "1999"},
  {"MM", "08"},
  {"DD", "12"},
  {0, 0},
};

int main(void)
{
  char str[128];
  struct assoc *ap;

  strcpy(str, "\"AAA,BBB,YY/MM/DD\"");
  printf("置換前 = %s\n", str);

  for (ap = Assoc; ap->label; ap++) {
    printf("%s → %s\n", ap->label, ap->string);
    change(str, ap->label, ap->string);
  }

  printf("置換後 = %s\n", str);
}

void change(char *buf, const char *label, const char *string)
{
  char *start, *end;
  char work[128];

  while (1) {
    start = strstr(buf, label);
    if (!start) break;
    end = start + strlen(label);
    switch (*end) {
      case ',':
      case '/':
      case '"':
      case '\0':
        strncpy(work, buf, start - buf);
        work[start - buf] = '\0';
        strcat(work, string);
        strcat(work, end);
        strcpy(buf, work);
        continue;
      default:
        continue;
    }
  }
}


No.2469

Re:文字列置換の上手な方法
投稿者---たか(2002/08/22 20:06:14)


あ、カンマで区切ってそれぞれ構造体に設定するのでしたか。
それは簡単にできますよね。省略します(^^;)

No.2471

Re:文字列置換の上手な方法
投稿者---たか(2002/08/22 20:24:20)


すみません、この掲示板に初めて書き込んだもので、前の2レスは削除
したかったのですが、削除できませんでした。

#include <stdio.h>
#include <string.h>

void change(char *buf, const char *label, const char *string);

struct assoc {
  char *label;
  char *string;
} Assoc[] = {
  {"AAA", "aaa"},
  {"BBB", "bbb"},
  {"YY", "1999"},
  {"MM", "08"},
  {"DD", "12"},
  {0, 0},
};

struct date {
  char s1[128];
  char s2[128];
  char day[128];
} Date;

int main(void)
{
  char str[128], *p;
  struct assoc *ap;

  strcpy(str, "AAA,BBB,YY/MM/DD");
  printf("置換前 = %s\n", str);

  for (ap = Assoc; ap->label; ap++) {
    printf("%s → %s\n", ap->label, ap->string);
    change(str, ap->label, ap->string);
  }

  printf("置換後 = %s\n", str);

  p = strtok(str, ",");
  strcpy(Date.s1, p);
  p = strtok(NULL, ",");
  strcpy(Date.s2, p);
  p = strtok(NULL, "");
  strcpy(Date.day, p);

  printf("s1 = %s, s2 = %s, day = %s\n", Date.s1, Date.s2, Date.day);

  return 0;
}

void change(char *buf, const char *label, const char *string)
{
  char *start, *end;
  char work[128];

  while (1) {
    start = strstr(buf, label);
    if (!start) break;
    end = start + strlen(label);
    switch (*end) {
      case ',':
      case '/':
      case '"':
      case '\0':
        strncpy(work, buf, start - buf);
        work[start - buf] = '\0';
        strcat(work, string);
        strcat(work, end);
        strcpy(buf, work);
        continue;
      default:
        continue;
    }
  }
}


No.2474

Re:文字列置換の上手な方法
投稿者---yukki(2002/08/22 21:19:39)


考えてみました。

change()の中でstrstrするときに先頭から検索するからいけないんですよね。
前に見つかった場所の次から検索すればいいのかな。

ちょっと考えてみます。

No.2476

Re:文字列置換の上手な方法
投稿者---たか(2002/08/22 23:48:48)


>change()の中でstrstrするときに先頭から検索するからいけないんですよね。

そうですね。bufの中を走査するポインタを作っておき、置換した文字列
の所は二度と調べないようにして、どこかに書き込んでいき、最後に
一度だけbuf[]に書き戻すといいですね。

No.2473

Re:文字列置換の上手な方法
投稿者---kikk(2002/08/22 20:58:19)


ども。


安直な方法。

>1.strchr()を使用して","を検索する

の代わりに、strcspn()を使ってはどうでしょうか? 第2引数を",/"として。
なお、返り値はstrchr()と違い、第1引数のアドレスに対するオフセットに
なります。


では。

No.2475

Re:文字列置換の上手な方法
投稿者---かずま(2002/08/22 22:00:52)


置換だけです。
#include <stdio.h>
#include <string.h>
#include <ctype.h>

typedef struct { char *name, *string; } Pair;

Pair table[] = {
    { "AAA", "aaa" }, { "BBB", "bbb" }, { "YY", "1999" },
    { "MM",  "08"  }, { "DD",  "12"  }, { NULL,  NULL  }
};

void substitute(const char *src, char *dst)
{
    char *name;
    Pair *p;

    for (;;) {
        while (!isalpha((unsigned char)*src))
            if ((*dst++ = *src++) == '\0') return;
        name = dst;
        while (isalpha((unsigned char)*src))
            *dst++ = *src++;
        *dst = '\0';
        for (p = table; p->name; p++) {
            if (strcmp(p->name, name) == 0) {
                strcpy(name, p->string);
                dst = name + strlen(p->string);
                break;
            }
        }
    }
}

int main(void)
{
    char *str = "AAA,BBB,YY/MM/DD";
    char buf[256];

    puts(str);
    substitute(str, buf);
    puts(buf);
    return 0;
}


No.2477

Re:文字列置換の上手な方法
投稿者---かずま(2002/08/23 01:08:38)


> 置換だけです。

では、構造体に入れてみましょう。
int main(void)
{
    char *str = "AAA,BBB,YY/MM/DD";
    char buf[256];
    struct { char m0[64], m1[64], m2[64]; } data;

    puts(str);
    substitute(str, buf);
    puts(buf);
    if (sscanf(buf, "%63[^,],%63[^,],%63[^,]", data.m0, data.m1, data.m2) == 3)
        printf("m0=%s, m1=%s, m2=%s\n", data.m0, data.m1, data.m2);
    return 0;
}