文字列検索について

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

文字列検索について

#1

投稿記事 by 凡人@sos » 2年前

[1] 質問文
 [1.1] 自分が今行いたい事は何か   指定した文字列を表示させるプログラム
 [1.2] どのように取り組んだか(プログラムコードがある場合記載)
  

コード:

  #include <stdio.h>
    char test1[4];
    char test2[16] = "sos filenonfile";
    int main(void){
    printf("文字入力せよ:\n");
    scanf("%s", test1);
    result ();
    return 0;
     }
  
  
 [1.3] どのようなエラーやトラブルで困っているか(エラーメッセージが解る場合は記載)
 [1.4] 今何がわからないのか、知りたいのか   
         たとえば、scanfで、file と入力した際に、デバッグをしたさいに、
        「4文字目から定義されています。」といった判定が出来るぷろぐらむを作りたいのですが、
         難しい構文等を調べたのですが、全く分からなかったので、
          プログラムの全容を教えて頂きたく、掲載させて頂きました。

[2] 環境  
 [2.1] OS : Windows10
 [2.2] コンパイラ  visual stdio 2017 c++

[3] その他
 ・どの程度C言語を理解しているか 基本的な動作しか出来ません。c言語を始めたばかりで、独学です。
 

アバター
purin52002
記事: 235
登録日時: 2年前
連絡を取る:

Re: 文字列検索について

#2

投稿記事 by purin52002 » 2年前

こんにちは

「c言語 match」で検索をかけたところ、 strstr という関数が用意されているようです。
文字列test2 の中から 文字列test1 とマッチングする箇所を判定したいのだとしたら、

コード:

test1に文字列を入力
char型のポインタ p に strstr(test2, test1) の結果を代入する
(strstr関数は test2 の中から test1 を検索し、見つかったらその位置のポインタを返す。見つからなかったらNULL)
p がNULLだったら
  「見つかりませんでした」と表示
p がNULLじゃなかったら
  p と test2 の先頭アドレスの差を int型のindex に代入
  (引き算してchar型のsizeで割る(でいける?)) *1
  「index 文字目から定義~」と表示
のような感じでいける気がします。

コード:

*1 アドレスの差を求める別の方法

test2 の先頭アドレスを char型のポインタ p2 に代入
p と p2 が一致しない間、 int型のcnt をインクリメント
p と p2 が一致したら、 cnt がアドレスの差になってる(、、、はず^p^)
c++初心者を自負しています。
質問者さんには今後私にプログラミングを教えてくれるようにやさしく丁寧に教えるつもりです。ぎぶあんどていく^p^
回答者さんには精一杯感謝します。ぎぶおんりー^p^

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

Re: 文字列検索について

#3

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

凡人@sos さんが書きました:たとえば、scanfで、file と入力した際に、デバッグをしたさいに、
        「4文字目から定義されています。」といった判定が出来るぷろぐらむを作りたいのですが、
まずは、fileは終端のヌル文字を含めて5文字なので、4要素しかないtest1には入り切らず、範囲外への書き込みが発生して未定義動作になります。
test1の要素数を増やすべきでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: 文字列検索について

#4

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

purin52002 さんが書きました:

コード:

  p と test2 の先頭アドレスの差を int型のindex に代入
  (引き算してchar型のsizeで割る(でいける?)) *1
  「index 文字目から定義~」と表示
C言語では、ポインタ同士の引き算は配列の添字の差を返すと定義されているので、char型のサイズで割る必要はありません。
char型のサイズは1と定義されているので、割っても間違った結果にはなりません。
purin52002 さんが書きました:

コード:

*1 アドレスの差を求める別の方法

test2 の先頭アドレスを char型のポインタ p2 に代入
p と p2 が一致しない間、 int型のcnt をインクリメント
p と p2 が一致したら、 cnt がアドレスの差になってる(、、、はず^p^)
p2を更新しないと、無限ループになってint型のオーバーフローによる未定義動作が発生するかもしれません。
cntの初期化も必要ですね。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Math

Re: 文字列検索について

#5

投稿記事 by Math » 2年前

Windows10, Visual Stdio 2017 C言語, 開発者コマンドプロンプト使用

char の配列の初期化は 次のようにできます。 
char str[] = "abc";

これは
char str[] = {'a','b','c','\0'};
のシンタックスシュガーです。

シンタックスシュガーはhttp://dixq.net/forum/viewtopic.php?f=3&t=19257
など過去ログで何度もかいてます。

c.bat

コード:

rem コンパイル後リンク
cl /TC c1.c
rem 実行結果
c1.exe
c1.c

コード:

#include <stdio.h>
char test1[] = "file"; // char test1[4];==>コンパイラに数えさせる方が安全:test1[5]
char test2[] = "sos filenonfile"; // char test2[16] = "sos filenonfile";==>同上tese2=[17]

int result(){

	int n1=4,n2=16,i=0;
	
	for(i=0; (16-i) > 4; i++){
		printf("%d\t",i);
		printf("%c : %c\t",test2[i + 0],test1[0]);
		printf("%c : %c\t",test2[i + 1],test1[1]);
		printf("%c : %c\t",test2[i + 2],test1[2]);
		printf("%c : %c\n",test2[i + 3],test1[3]);

		if ( test2[i + 0]==test1[0] && 
		     test2[i + 1]==test1[1] && 
		     test2[i + 2]==test1[2] && 
		     test2[i + 3]==test1[3] ){

		    printf("%d文字目から定義されています",i); 
		    break; 
		}
	}
	return 0;
}

int main(void) {
	printf("文字入力せよ:\n");
	//scanf("%s", test1);==> file が入力されるものと仮定
	result();
	return 0;
}
実行

コード:

D:\z17c\c\0730>rem コンパイル後リンク

D:\z17c\c\0730>cl /TC c1.c
Microsoft(R) C/C++ Optimizing Compiler Version 19.10.25019 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

c1.c
Microsoft (R) Incremental Linker Version 14.10.25019.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:c1.exe
c1.obj

D:\z17c\c\0730>rem 実行結果

D:\z17c\c\0730>c1.exe
文字入力せよ:
0       s : f   o : i   s : l     : e
1       o : f   s : i     : l   f : e
2       s : f     : i   f : l   i : e
3         : f   f : i   i : l   l : e
4       f : f   i : i   l : l   e : e
4文字目から定義されています
D:\z17c\c\0730>

box
記事: 1739
登録日時: 9年前

Re: 文字列検索について

#6

投稿記事 by box » 2年前

Math さんが書きました:

コード:

#include <stdio.h>
char test1[] = "file"; // char test1[4];==>コンパイラに数えさせる方が安全:test1[5]
char test2[] = "sos filenonfile"; // char test2[16] = "sos filenonfile";==>同上tese2=[17]
せっかくこう書くのであれば、
Math さんが書きました:

コード:

	int n1=4,n2=16,i=0;
	
	for(i=0; (16-i) > 4; i++){
このあたりもコンピューターに数えてもらう方がよくないですか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

Math

Re: 文字列検索について

#7

投稿記事 by Math » 2年前

C#だと簡単だけどCではどうかくのか 書いてやって ください。思いつかない…。簡単にかけないなら質問者様には主意は十分通じるので余計なことはいまはいいとはおもいますかけど(^^;

Math

Re: 文字列検索について

#8

投稿記事 by Math » 2年前

[char 補足:参考]
実際のプログラムでは配列を文字列で初期化しなければならないケースはそれほどなくたいていの場合は char *str="abc"; と書くことで用はたりるとおまいます。
この場合[char の配列の初期化] でなく char へのポインター を 文字列リテラルで初期化しているので 文字列の内容は書き換えることができません。大抵文字列リテラルは関数(プログラム)本体と同じ領域にマッピングされます。

かずま

Re: 文字列検索について

#9

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

result() に引数を付けたので、質問の解答ではありませんが、
これを参考にあなたの望んでいるプログラムが書けると思います。

コード:

#include <stdio.h>

char test1[1000];
char test2[] = "sos filenonfile";
 
int result(const char *s1, const char *s2)
{
    int i, j;
    for (i = 0; s1[i]; i++)
        for (j = 0; ; j++) {
            if (s2[j] == '\0') return i;
            if (s2[j] != s1[i+j]) break;
        }
    return -1;
}
 
int main(void)
{
    int i;
    printf("test2: %s\n", test2);
    puts("文字入力せよ:");
    scanf("%s", test1);
    i = result(test2, test1);
    if (i >= 0)
        printf("'%s' starts at test2[%d]\n", test1, i);
    else
        printf("'%s' not found\n", test1);
}
このプログラムについて、わからないことがあれば
質問を受け付けます。

他の人のアドバイスでも、わからないことがあれば
どんどん質問しましょう。
だまっていると、何も進みません。

凡人@sos

Re: 文字列検索について

#10

投稿記事 by 凡人@sos » 2年前

こちらの場合、const char という形に直さなければならないのでしょうか?
又、int main (void){
の文中で、forループを回して表示させる方法が、やはり思いつきませんが、
たとえば、どのように定義することが出来ればよいのでしょうか?





かずま さんが書きました:result() に引数を付けたので、質問の解答ではありませんが、
これを参考にあなたの望んでいるプログラムが書けると思います。

コード:

#include <stdio.h>

char test1[1000];
char test2[] = "sos filenonfile";
 
int result(const char *s1, const char *s2)
{
    int i, j;
    for (i = 0; s1[i]; i++)
        for (j = 0; ; j++) {
            if (s2[j] == '\0') return i;
            if (s2[j] != s1[i+j]) break;
        }
    return -1;
}
 
int main(void)
{
    int i;
    printf("test2: %s\n", test2);
    puts("文字入力せよ:");
    scanf("%s", test1);
    i = result(test2, test1);
    if (i >= 0)
        printf("'%s' starts at test2[%d]\n", test1, i);
    else
        printf("'%s' not found\n", test1);
}
このプログラムについて、わからないことがあれば
質問を受け付けます。

他の人のアドバイスでも、わからないことがあれば
どんどん質問しましょう。
だまっていると、何も進みません。

かずま

Re: 文字列検索について

#11

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

凡人@sos さんが書きました:こちらの場合、const char という形に直さなければならないのでしょうか?
こちらとは、どちらでしょうか?

int result(const char *s1, const char *s2) は、
int result(char *s1, char *s2) でも構いません。

それとも、引数なしの result() がダメかという質問ですか?
もちろん、引数なしにもできます。それは、No.5 の Mathさんの
プログラムなどを参考に、あなたにやってもらいたかったのです。
s1 を test2 に、s2 を test1 に置き換えるだけです。

コード:

int result(void)
{
    int i, j;
    for (i = 0; test2[i]; i++)
        for (j = 0; ; j++) {
            if (test1[j] == '\0') return i;
            if (test1[j] != test2[i+j]) break;
        }
    return -1;
}
もちろん、呼出しの i = result(test2, test1); を i = result(); に変えます。
凡人@sos さんが書きました: 又、int main (void){
の文中で、forループを回して表示させる方法が、やはり思いつきませんが、
たとえば、どのように定義することが出来ればよいのでしょうか?
質問の意味がよくわかりません。
main を次のようにしたいということですか?

コード:

int main(void)
{
    int i, k;
    printf("test2: %s\n", test2);
	for (k = 1; k <= 5; k++) {
		printf("[%d] 文字入力せよ:\n", k);
		scanf("%s", test1);
		i = result(test2, test1);
		if (i >= 0)
			printf("'%s' starts at test2[%d]\n", test1, i);
		else
			printf("'%s' not found\n", test1);
	}
	return 0;
}
実行結果

コード:

test2: sos filenonfile
[1] 文字入力せよ:
sos
'sos' starts at test2[0]
[2] 文字入力せよ:
file
'file' starts at test2[4]
[3] 文字入力せよ:
non
'non' starts at test2[8]
[4] 文字入力せよ:
nf
'nf' starts at test2[10]
[5] 文字入力せよ:
os
'os' starts at test2[1]
他の回答者のアドバイスも参考に、例えば strstr() を使った
プログラムなどにもチャレンジしてください。

かずま

Re: 文字列検索について

#12

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

凡人@sos さんが書きました: 又、int main (void){
の文中で、forループを回して表示させる方法が、やはり思いつきませんが、
たとえば、どのように定義することが出来ればよいのでしょうか?
関数 result を定義せず、main の中だけで処理したいということでしょうか?

コード:

#include <stdio.h>
 
char test1[1000];
char test2[] = "sos filenonfile";
 
int main(void)
{
    int i, j = 0;

    printf("test2: %s\n", test2);
    puts("文字入力せよ:");
    scanf("%s", test1);
    for (i = 0; test2[i]; i++) {
        for (j = 0; test1[j] && test1[j] == test2[i+j]; j++) ;
        if (test1[j] == '\0') break;
    }
    if (test1[j])
        printf("'%s' not found\n", test1);
    else
        printf("'%s' starts at test2[%d]\n", test1, i);
    return 0;
}

返信

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