質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

4回答

1225閲覧

2つの文字列の変換をしたいです

ht3433

総合スコア19

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

1グッド

1クリップ

投稿2019/10/02 07:20

前提・実現したいこと

2つの文字列の変換をしたいです。Linuxでいうtrの機能をつくりたいです。
例として、
tr asd zxc
a
z
というふうに文字が変換されるプログラムを作りたいです。
ですが、下記のソースコードを実行すると、
tr asd zxc
a
a
というふうに文字が変換されません。
以前にも同じような質問をして、いただいた回答を参考にコーディングしてみましたが、限界を感じたので質問することにしました。
大変恐縮ではございますが、お力添えいただければ幸いです。
よろしくお願いいたします。

該当のソースコード

C言語

1 2#include<stdio.h> 3#include<string.h> 4#include<ctype.h> 5 6char moji1[100], moji2[100], moji3[100], moji4[100]; 7char *pmoji2, *pmoji3, *pmoji4; 8int tr(){ 9 int i; 10 while( 1 ){ 11 // 値の入力 12 scanf( "%s", moji4 ); 13 pmoji2 = moji2; 14 pmoji3 = moji3; 15 pmoji4 = moji4; 16 17 for( i = 0; i<='\0'; ++i ){ 18 if( strcmp( ( pmoji4+i ), (pmoji2+i ) ) == 0 ){ 19 if( strcmp( ( pmoji2+i ), (pmoji3+i ) ) != 0 ){ 20 printf( "%s\n", ( pmoji3+i ) ); 21 }else{ 22 printf( "%s\n", ( pmoji4+i ) ); 23 } 24 }else{ 25 printf( "%s\n", ( pmoji4+i ) ); 26 } 27 } 28 } 29 return( 0 ); 30} 31 32int main(){ 33 // 値の入力 34 scanf( "%s %s %s",moji1, moji2, moji3 ); 35 // trメソッドの呼び出し 36 if( strcmp( moji1, "tr" ) == 0 ){ 37 tr(); 38 } 39}
DrqYuto👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2019/10/02 07:30

何か、与えられた課題?なのでしょうか。それとも自分の勉強の為、でしょうか。それによって回答のされ方が分かってくるかもしれません。
ht3433

2019/10/02 07:35

回答いただき、ありがとうございます! 自分のための勉強です。 きっかけは、知人にtrのプログラムをつくる練習をすれば、力がつくといわれやっています。なにかヒントをいただければ幸いです。
jimbe

2019/10/02 07:36

前のご質問に回答が付いていますので, その回答に対してコメントでお聞きになったほうがよいのではないでしょうか.
guest

回答4

0

strcmp は, 指定したアドレスから '\0' までを比較します.

prinf の "%s" は, 指定したアドレスから '\0' までを表示します.

文字列のバイト数は, strlen で取得できます.

投稿2019/10/02 07:50

jimbe

総合スコア12632

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ht3433

2019/10/02 07:58

回答していただき、ありがとうございます! 文字列のバイト数をstrlen で取得できるとありますが、取得した文字列のバイト数で比較するということでしょうか?
jimbe

2019/10/02 15:58

文字列( C では文字配列)内の個々の文字を比較する際は, ループカウンタが 0 から文字数まで変化するループを組み, ループカウンタを文字位置(配列添字)の指定に使用するのが定石です. (例) for(i=0;i<strlen(moji4);++i) { if(moji4[i] == ~ ) { ~ } ~ } プログラミングは、既存の部品から(部品からさらに部品を作りながら)完成品を組み立てる行為です. 基本である C 言語の理解(ポインタ・配列・if 文・for 文等), 各部品(strcmp, printf 等)の動作を理解し, 完成品( tr コマンド)にはどの部品をどう組み合わせるのかを決定しなければなりません. ご提示のコードやこれまでの経緯を見る限り, 言語の理解も部品の理解も不足しているように思われます. ご友人に tr コマンドを題材に勧められたそうですが, 独学よりも, 学習サイト等で基本から学ばれたほうが良いように思います.
guest

0

あまり参考にならないかもしれませんが。

C

1#include <stdio.h> // getchar, putchar 2#include <string.h> // strlen 3 4#define T2(x) x,x+1,x+2,x+3 5#define T1(x) T2(x),T2(x+4),T2(x+8),T2(x+12) 6#define T(x) T1(x),T1(x+16),T1(x+32),T1(x+48) 7unsigned char t[256] = { T(0), T(64), T(128), T(192) }; 8 9int main(int argc, char *argv[]) 10{ 11 if (argc != 3 || strlen(argv[1]) != strlen(argv[2])) 12 return fprintf(stderr, "usage: %s string1 string2\n", argv[0]), 1; 13 unsigned char b; 14 for (int i = 0; b = argv[1][i]; i++) t[b] = argv[2][i]; 15 int c; 16 while ((c = getchar()) != EOF) putchar(t[c]); 17}

追記
こう書けば参考になるのかな。

C

1#include <stdio.h> // getchar, putchar 2#include <string.h> // strlen 3 4unsigned char t[256] = { 5 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 6 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 7 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 8 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 9 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 10 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 11 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, 12 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, 13 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 14 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 15 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 16 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 17 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 18 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 19 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 20 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, 21}; 22 23int main(int argc, char *argv[]) 24{ 25 if (argc != 3 || strlen(argv[1]) != strlen(argv[2])) { 26 fprintf(stderr, "usage: %s string1 string2\n", argv[0]); 27 return 1; 28 } 29 int c; 30 unsigned char b; 31 for (c = 0; (b = argv[1][c]) != '\0'; c++) 32 t[b] = argv[2][c]; 33 while ((c = getchar()) != EOF) 34 putchar(t[c]); 35}

投稿2019/10/04 03:15

編集2019/10/04 21:10
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

このままだと収束しない気がしたので、僭越ながら、回答をひとつ。

というふうに文字が変換されません。

当たり前のことを書きますが、変換されないのは、「変換されないようにコードを書いているから」です。
いっぺんに色々なことが出てきて混乱しているかもしれませんが、これまで使ったことが無い未知の関数、scanfstrcmpの動作内容は把握されていますか? 何となく使って、出てきた結果のつじつま合わせをしていませんか?

まず頭の中で動きを確実にイメージしましょう。自分の頭と手と紙とペンを使ってその処理を逐一実行するととして、できますか? できたら、それをソースコードに反映するだけです。(実際は「だけ」では済みませんが) もし、trコマンドの実現が手に余ると言うのであれば、まだ課題としては難しいのかもしれません。

先のご質問 linuxのtrコマンドのような機能をつくりたいです の回答でアドバイスをいただいた件、
「文字の配列を1文字ずつ処理する方法」をマスター。。。あるいはマスターとまではいかないくても、作って、やってみましたか?それが出来れば、trへの応用もそんなに難しいことではないはずなのです。悔しいかもしれませんが、もう一歩戻るのも長い目で見て近道です。

ちなみに、下は簡易版 tr コマンドを私が作ってみた例です。コードは、あえて冗長に書いたところがあります。「こんな風にも書ける」程度に見てください。もっと良い例もあるでしょうし、ht3433 さんがこれを流用する必要はありません。ただ、もし中身が理解できなければ、まだ早いと言うことです。(失礼だったらごめんなさい)
バグや問題点のご指摘は歓迎しますが、中身の解説の依頼はご遠慮ください。

変換元のと変換先の文字列集合の指定には、コマンドラインオプションmain(int argc, char *argv[])を使います。尚、全角文字は扱えません。

C

1/* simple_tr.c */ 2 3#include <stdio.h> 4#include <string.h> 5 6int translate(int cc, const char *ps1, const char *ps2) { 7 while (*ps1 != '\0') { 8 if (*ps1 == cc) { 9 return *ps2; 10 } 11 ++ps1; 12 ++ps2; 13 } 14 return cc; 15} 16 17int main(int argc, char *argv[]) { 18 if (argc < 3) { 19 fprintf(stderr, "Too few argument(s). ABORT!\n"); 20 return 1; 21 } 22 23 if (strlen(argv[1]) != strlen(argv[2])) { 24 fprintf(stderr, "Arg1 and arg2 must be same length! ABORT!\n"); 25 return 1; 26 } 27 28 int cc; 29 for (;;) { 30 cc = getchar(); 31 if (cc == EOF) { 32 break; 33 } 34 cc = translate(cc, argv[1], argv[2]); 35 putchar(cc); 36 } 37 38 return 0; 39}

Cygwin上でのコンパイル~実行例です。

Bash

1$ gcc -Wall simple_tr.c -o simple_tr.exe 2 3$ ./simple_tr.exe asd zxc 4a 5z 6s 7x 8d 9c 101 111 122 132 143 153 16123asd789 17123zxc789 18123asdASD 19123zxcASD 20

投稿2019/10/02 10:38

dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ht3433

2019/10/04 01:19

回答していただき、ありがとうございます! dodox86さんに指摘された事を参考にソースコードを書き直し、 ./tr asd zxc と実行し、 asd と入力しましたが、 という結果になってしまい、文字が表示されません。 自分なりに考えたのですが、なにが原因か分かりませんでした。 ご教授いただければ幸いです。 ソースコード #include<stdio.h> #include<string.h> #include<ctype.h> int main(int argc, char *argv[]){ // 入力する文字の変数を定義 char moji1[100]; // 何度も入力可能にする while( 1 ){ // 文字の入力 scanf( "%s", moji1 ); // 入力した文字分だけ繰り返す for(size_t i = 0; i < strlen( moji1 ); i++ ){ // trの変換先の文字分だけ繰り返す for(size_t j = 0; j < strlen( argv[1] ); j++ ){ // 入力した文字と変換された文字が同じか判定 if( moji1[i] == *( argv[1] +j ) ){ // trの変換先と変換元の文字が同じか判定 if( *( argv[1] +j ) != *( argv[2] +j ) ){ printf( "%c", *( argv[2] +j ) ); }else{ printf( "%c", moji1[i] ); } }else{ printf( "%c", moji1[i] ); } } } } return( 0 ); }
dodox86

2019/10/04 01:55 編集

> ./tr asd zxc と実行し、 > asd と入力しましたが、 > > という結果になってしまい、文字が表示されません。 ?? scanfの実行でENTERキーの入力待ちになっているだけでは? > 自分なりに考えたのですが、なにが原因か分かりませんでした。 それでは困ります。もし、printfなどを使ったデバッグ用の出力でプログラムの動きが分からないのであれば、ある意味それも「限界」なので、Visual Studio などのデバッガーを使ってステップ実行や変数の中身を見て、想定どおりの動きになっているか確認しましょう。それで理解も深まると思います。(インストール方法や使い方の詳細は、ここでは聞かないでくださいね)
ht3433

2019/10/04 05:48

自分でデバックをしたり、for文の復習などをすると、ついに解決することができました。 証拠として、ソースコードをのせます。 もし、できていないところがありましたら、ご指摘いただけるとありがたいです。 #include<stdio.h> #include<string.h> #include<ctype.h> int main(int argc, char *argv[]){ // 入力する文字の変数を定義 char moji1[100]; size_t i; size_t j; // 何度も文字を入力可能にする while( 1 ){ // 文字の入力 scanf( "%c", moji1 ); // 入力した文字分だけ繰り返す for( i = 0; i < strlen( moji1 ); i++ ){ // trの変換先の文字分だけ繰り返す for( j = 0; j < strlen( argv[1] ); j++ ){ // 入力した文字と変換先の文字が同じか判定 if( moji1[i] == argv[1][j] ){   // trの変換先と変換元の文字が同じか判定 if( argv[1][j] != argv[2][j] ){ printf( "%c", argv[2][j] ); i++; }else{ printf( "%c", moji1[i] ); } } } if( moji1[i] != argv[1][j] ){ printf( "%c", moji1[i] ); } } } return( 0 ); }
dodox86

2019/10/04 06:07

惜しいようですが、NGな部分があります。 1. いつプログラムは終われるのか? whileから抜けてきません。 2. 1より大きい問題は、scanf("%c")の入出力です。"%c" で受けた場合はNULL文字はお尻に入りません。 以下のようにしてみると問題が発覚します。 while( 1 ){    memset(moji1, '*', sizeof(moji1)); // 文字の入力 scanf( "%c", moji1 ); printf("len=%d\n", strlen(moji1)); printf("[%s]\n", moji1); // 入力した文字分だけ繰り返す ... scanf("%c")を使うべきか、と言う問題もあります。使って使えない訳ではないですけれど。
ht3433

2019/10/04 06:44

1.trコマンドと同じようにしたかったので、あえてwhileからぬけられないようにしています。 2.NULL文字がはいっていないとなぜいけないのでしょうか? また、実行しておかしなところがあるのでしょうか?
dodox86

2019/10/04 06:55

> 1.trコマンドと同じようにしたかったので、あえてwhileからぬけられないようにしています。 本物のtrコマンドはWindowsのコマンドプロンプトならCTRL+Z、UNIX端末ならCTRL+D で抜けます。これはこれらターミナル上でのEOFに相当します。また、ファイルを読み込ませても終わり(EOF)を正しく検知します。 ですが、今のプログラムは勉強用だと思うので、大きな問題ではないと思います。ht3433さんの好きなように実装してください。 > NULL文字がはいっていないとなぜいけないのでしょうか? ご自分のプログラムで、以下のようにprintfを使っていますよね?正しく文字列終端を'\0'で埋めないと、ゴミを出力してしまいます。 > printf( "%c", moji1[i] ); > また、実行しておかしなところがあるのでしょうか? 実際に試していませんか? 例えば以下のようになります。'*'が、ゴミ相当です。(len=とか、デバッグ出力を追加していますけど) $ ./t2b.exe abc 123 abcefg len=107 [a***************************************************************************************************da(]n1***************************************************************************************************len=101 [b***************************************************************************************************]n2***************************************************************************************************len=101 [c************************************************ 逆にこれをmemsetで最初に常に0で埋めれば、問題なくht3433さんが想定している動きになります memset(moji1, 0, sizeof(moji1));
ht3433

2019/10/04 08:26

詳しい解説ありがとうございます! 大変勉強になり、おかげでなんとか自分が作りたいものができました。 ありがとうございました。
dodox86

2019/10/04 08:36

私も当初、少し辛らつな言葉で回答したので嫌にならないかとその後を心配していましたが、まずは目的を達成できてよかったです。他の方の回答、コメントも意識して、引き続きがんばってください。
ht3433

2019/10/04 08:54

ありがとうございます! これからも頑張ります!
ht3433

2019/10/09 04:58

先日は大変お世話になりました。 あれから、さらに完成度の高いものにしたいと思い、EOFを使ってCTRL+D で抜けるようにしてみたのですが、下記のようになり上手くいきませんでした。 ./tr abc def abc aec 何かヒントをいただけるとありがたいです。 よろしくお願いいたします。 ソースコード #include<stdio.h> #include<string.h> #include<ctype.h> int main(int argc, char *argv[]){ // 入力する文字の変数を定義 char moji1[100]; int c; size_t i; size_t j; // 何度も文字を入力可能にする while( ( c = getchar() ) != EOF ){ putchar( c ); memset(moji1, 0, sizeof(moji1)); // 文字の入力 scanf( "%c", moji1 ); // 入力した文字分だけ繰り返す for( i = 0; i < strlen( moji1 ); i++ ){ // trの変換先の文字分だけ繰り返す for( j = 0; j < strlen( argv[1] ); j++ ){ // 入力した文字と変換先の文字が同じか判定      if( moji1[i] == argv[1][j] ){ // trの変換先と変換元の文字が同じか判定 if( argv[1][j] != argv[2][j] ){ printf( "%c", argv[2][j] ); i++; }else{ printf( "%c", moji1[i] ); } } } if( moji1[i] != argv[1][j] ){ printf( "%c", moji1[i] ); } } } return( 0 ); }
dodox86

2019/10/09 05:06

それはもう別の問題だと思います。一度閉じた質問に積み重ねて新たな質問をコメントで上げるのはちょっとマナーとしてどうか、と思いました。ヒントだけ書きますが、getchar()を使ってputchar()した後、尚、scanf("%c)を使って制御するのは意図どおりなのでしょうか。ご自分で書いたプログラムなのですから、流れをよく検討しましょう。
dodox86

2019/10/09 05:09

その前に、試している環境はUNIX(linux)なのでしょうか。先のコメントで、「WindowsのコマンドプロンプトならCTRL+Z、UNIX端末ならCTRL+D で抜けます。」と書きました。
ht3433

2019/10/09 05:12

試している環境は、linuxです。 分かりました。ヒントありがとうございます。 もしわからなくなったら、新しく質問いたします。
guest

0

きのついたとこだけ。

for( i = 0; i<='\0'; ++i ){

'\0' というのは0ってことなので、このループはiが0のときしか回りません
#最初の1回だけしか回らない

投稿2019/10/02 07:38

y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ht3433

2019/10/02 08:22

回答していただき、ありがとうございます! ¥0から3などの数字にすると、回るようになりました。 ただ、回ってもうまく変換されませんでした。 原因を考えたのですが、分かりませんでした。 なにかヒントをいただけるとありがたいです。
y_waiwai

2019/10/02 08:32

そこのループは、どういう条件のときに止めればいいのかを考えましょう #て、そもそも、提示のコードは質問の動作はしないのでもとから考え直しましょう
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問