weemiee
@weemiee (weemiee)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

【C言語】文字列の中で文字列の置き換え作業をしたい

Q&A

Closed

1行の文字列が標準入力によって入力された時、その文字列に含まれる「yes」を「no」に置き換えて出力するプログラム」を考えていました。

例:
「yes i do not」→「no i do not」
「employees」→「employees」
「superyesman」→「supernoman」

僕がコンパイル・実行した最後のプログラムが以下のものです。

失敗したプログラム
#include <stdio.h>
#include <string.h>

int main(){
	
	char buf[1000];
	char s[100];
	char wrong_str[]="yes";
	char part[100]="";
	char head[100]="";
	
	fgets(buf, sizeof(buf), stdin);
	sscanf(buf, "%s", s);
	
	int j,k;
	
	//③①がつきあたりに行くまで行う
	int count=0;
	int past_wrong_count=0;
	while(count<strlen(s)-strlen(wrong_str)-1){
		
		//①Falseの文字数分だけsから文字を順番に抜き取った文字列partを作り…
		for(j=0;j<strlen(wrong_str);j++){
			char chr[2];
			sprintf(chr,"%c",s[count+j]);
			strcat(part,chr);
		}
		
		//②文字列一致するか調べる作業を
		int ret=strcmp(part,wrong_str);
		if(ret==0){
			for(k=past_wrong_count;k<count;k++){
				char chc[2];
				sprintf(chc,"%c",s[k]);
				strcat(head,chc);
			}
			strcat(head,"no");
			
			past_wrong_count=count+strlen(wrong_str);
			count=count+strlen(wrong_str);
			
		}else{
			char chc[2];
			sprintf(chc,"%c",s[count]);
			strcat(head,chc);
			count++;
		}
		
	}
	
	printf("%s\n",head);
	
}

実行結果は全くの期待外れで、色々と試行錯誤をしてみたものの、糸口が掴めないどころか煩雑化して考える要素がさらに増えたため、この考え方ではそもそも駄目なんだと思い 堪らずギブアップしてしまいました。
このプログラムを考えるだけで何日もかかっていて、一人では解決出来ず、インターンを間近にして非常に危機感を感じています。
このような「ある文字列に含まれる特定の文字列Aを、Aとは字数の異なる別の文字列Bに置き換えるプログラム」に対し、最もシンプルで考えやすい手段があれば是非知りたいです。また、文字列のM番目からN番目 (特にM≠1かつM<Nの場合) までの文字の並びを調べたい時 (文字列一致の判断等に使えるかと) に有効な方法もご教授頂きたいです。

0

5Answer

文字列操作ならstringライブラリを調べるといいですよ。

strncmp や strncpy を使えばシンプルになります。

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

int main() {

    char buf[1000];
    char head[1000];
    int head_index = 0;
    char wrong_str[] = "yes";
    int wrong_len = strlen(wrong_str);
    char correct_str[] = "no";
    int correct_len = strlen(correct_str);

    fgets(buf, sizeof(buf), stdin);

    int buf_last = strlen(buf) + 1;  // includes '\0' to copy it
    for (int buf_index = 0; buf_index < buf_last; ) {
        if (strncmp(buf + buf_index, wrong_str, wrong_len) == 0) {
            strcpy(head + head_index, correct_str);
            head_index += correct_len;
            buf_index += wrong_len;
        } else {
            head[head_index++] = buf[buf_index++];
        }
    }

    printf("%s\n", head);
}

strstrで文字列検索できます。

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

int main() {

    char buf[1000];
    char *buf_ptr = buf;
    char head[1000];
    char *head_ptr = head;
    char wrong_str[] = "yes";
    int wrong_len = strlen(wrong_str);
    char correct_str[] = "no";
    int correct_len = strlen(correct_str);

    fgets(buf, sizeof(buf), stdin);

    char *match_ptr;
    while ((match_ptr = strstr(buf_ptr, wrong_str)) != NULL) {
        strncpy(head_ptr, buf_ptr, match_ptr - buf_ptr);
        head_ptr += match_ptr - buf_ptr;
        strcpy(head_ptr, correct_str);
        head_ptr += correct_len;
        buf_ptr = match_ptr + wrong_len;
    }

    strcpy(head_ptr, buf_ptr);
    printf("%s\n", head);
}
2Like

自分もCは勉強中なので...

#include <stdio.h>
#include <string.h>
const char *WHAT_TO_LOOK_FOR = "yes";
const char *CHANGE_TO = "no";

int main() {
  const unsigned char WHAT_TO_LOOK_FOR_SIZE = strlen(WHAT_TO_LOOK_FOR);
  const unsigned char CHANGE_TO_SIZE = strlen(CHANGE_TO);

  char text[255];
  char new_text[255];
  unsigned short new_text_count = 0;
  fgets(text, sizeof(text), stdin);

  const unsigned char NEXT_X_LETTERS = WHAT_TO_LOOK_FOR_SIZE - 1;
  const unsigned char MAX_SIZE = strlen(text) - 2; // fgetsから来る\nを除去
  for (unsigned short count = 0; count <= MAX_SIZE; count++) {

    //終了パターン
    if (MAX_SIZE < count + NEXT_X_LETTERS) {
      for (unsigned char c = count; c <= MAX_SIZE; c++) {
        new_text[new_text_count] = text[c];
        new_text_count++;
      }
      new_text[new_text_count] = '\0';
      break;
    }

    // 現在の位置から2個先まで読んで保存する
    char buffer[WHAT_TO_LOOK_FOR_SIZE + 1];
    for (unsigned char c = 0; c <= WHAT_TO_LOOK_FOR_SIZE - 1; c++) {
      buffer[c] = text[count + c];
    }
    buffer[WHAT_TO_LOOK_FOR_SIZE] = '\0';

    // 未検出パターン
    if (strcmp(buffer, WHAT_TO_LOOK_FOR) != 0) {
      new_text[new_text_count] = text[count];
      new_text_count++;
      continue;
    }

    // 検出パターン
    for (unsigned char c = 0; c <= CHANGE_TO_SIZE - 1; c++) {
      new_text[new_text_count] = CHANGE_TO[c];
      new_text_count++;
    }
    count += NEXT_X_LETTERS;
  }

  printf("before: %s", text);
  printf("after:  %s", new_text);

  return 0;
}

引っかかった点としては

  • fgetsは"\n"を含んで返す
  • "\n"はstrlen(...)でカウントされる('\0'はされない)
  • 入力された文字列をそのまま編集しようとするととても難しいので、別変数を一緒にまわす
  • 入力された文字列を1文字ずつ取り出し、2個先と合わせてみて自分が探している文字かを確認
  • 探している文字なら別変数側に置換したい文字を挿入、入力された文字列側のカウンターは2個先に移動させておく
  • 2個先が入力された文字列を超えるなら、残り全ての文字を別変数側に突っ込む
  • 最後は'\0'を忘れない

1Like

@shiracamusさん、@github0013@githubさん、ご回答ありがとうございます。
僕の発見は、例えば@shiracamusさんのプログラム中のstrncmp(buf + buf_index,@github0013@githubさんのプログラム中の= text[count + c];のように、int型とchar型 (異なる型同士) を合算されている部分がある点でした。こんな事が出来るとは知らなかったので、驚きでした。

@shiracamusさんにはstring.hのライブラリを紹介頂き、知らなかった関数を今さらのように沢山目にしました。とはいえ、覚えるのは簡単でも、実際に使えるようになっていくには多くの経験値が要るはずなので、Qiitaでお力添えを受けながら腕を磨いていこうと思います。@shiracamusさんの教えてくださったプログラムは、確かにスッキリしていて見やすかったです。特にポインタ計算の部分が興味深かったので、仕組みや方法について調べてみようと思います。

@github0013@githubさんのプログラムが、僕の目指していたゴールに一番近かったように思います。自分がどこを分かっていなかったのか等、自分の書いたプログラムと見比べながら今一度見直してみようと思います。ただ、さっそくプログラムを動かしてみた時に一つだけ気になった点がありました。

上記URLのサイトでコンパイル・実行した結果、文字列「superyesman」を入力した後、最後尾の文字「n」が抜けた状態で出力された点です。原因は何かと考えていた時に、@github0013@githubさんが作成してくださったGoogleドキュメントの表において「NULL」が要素2個分を占めていたのが気になりました。プログラムにおいても、MAX_SIZEを定義する部分を

const unsigned char MAX_SIZE = strlen(text) - 1; // fgetsから来る\nを除去

と書き換えたら「n」も入ってくれました。そのため、個人的に、改行文字「\n」は文字列textの要素1個分のサイズではないかと考えました。何かアドバイス頂けると幸いです。

0Like

Screen Shot 2022-08-01 at 8.52.18.png

NULLは2要素分ではないです。Spreadsheetのセル値が右にはみ出てるだけです。

paizaでnが欠けているのは、input側で"superyesman\n"と最後に改行を入れていないからです。
fgetsだと標準入力でENTERを押して通常は入力をすると思うので、どうしても最後に'\n'が来るよな..って事でその分を除外しています。(普通ならtrim的な処理をするんでしょうが、C言語は全然わからないので)

なのでpaizaでもinputの最後に改行を入れればちゃんと変換されると思います。

0Like

@github0013@githubさん
そうなんですね。確かに、入力時に改行せず、そのまま実行してしまいました。これが原因だったのですね。
教えて頂き、ありがとうございます。

0Like

Your answer might help someone💌