- ベストアンサー
aから始まりzzzzで終わるためには・・
ファイル出力したら a b c . . . aa ab . . . aaa . . . . zzzz となるようにプログラムを書きたいのですがどうしたらいいのかわかりません。 C言語で記述しています。 4文字限定ならfor文四つでくるくる回したらいいと言うことは作ってみて理解できました。 ですが 数字で言う繰り上がりをどのようにしたらいいのかわかりません。具体例も入れて説明してくれると幸いです。コンパイラはgccです。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
>TABLE[]="abcdefghijklmnopqrstuvwxyz"; は、桁上げする文字の順序のリスト a-zと決まっている場合は、このように準備しないでもいいですが ADBCとか、コードにより決まる順番でなくてもよいという利点があります。 TABLESIZEは、"文字リスト"にしたために、\0をサイズから引く必要があります。 TABLE={'a','b',…};の場合は引く必要がありません。 関数 incStr は、渡されてた文字列(ポインタ)をリストに従って桁上げします。 リストは、関数の中にすべきかもしれませんが、外にあることで、変更できます。 >len=strlen(str); は、処理すべき文字列の長さを求めておきます。 >for(i=0;i<len;i++){ 処理すべき文字位置でループします。0~len-1ですべての文字を参照することができます。 ところで、桁上げ処理する時は、 "abc"の時 c の位置の文字から始めないといけないので、 i=0~len-1ですが、 str[len-1-i]とすることで、c, b, a の順に処理します。 >if(carry){ //桁上がりがある 処理は最悪、すべての桁を調べないといけませんが、 桁上げしないのならそれより上の桁は調べる必要がありません。 なので、carry:桁上がりが無い時はループから抜けます。 最初は、処理のキッカケとして処理するというところから 桁上げがあるということにして処理を始めます。 (というかこの関数はそもそも桁上げする処理) >if(str[len-1-i] == TABLE[TABLESIZE-1]){ str[len-1-i]は、処理中の桁 一番最初は例で言えば c です。 TABLE[TABLESIZE-1] は、リストの一番最後の文字 z です。 char d[5]は、d[0],d[1],d[2],d[3],d[4]; ですから d[SIZE]の時最後は、d[SIZE-1]になります。 今調べている文字がリストの最後の文字の時、 桁の繰り上がりをすることになります。 そうでない時には、 carry=0で、桁上げが無いことを記して >str[len-1-i] = *(strchr(TABLE, str[len-1-i])+1) リストの次の文字に置き換えます。 strchrは、指定した文字をリストから探してそのポインタを返します。 c の場合は&TABLE[2]を返すことになります。 既に、リストの最後の文字ではないことはわかっているので、 その隣の位置(+1)にポインタを進めます。 *(ポインタ)は、ポインタの位置の内容を取り出すという意味です。 つまり、c の時 d が取り出されて str[2]='d'; がされて "abc" が "abd" になることになります。 "abz" の様に桁上げが起こる場合(リストの最後の文字だった場合) >carry=1; 桁上げがあったことを記録して次の桁の処理にまかせます。 そして処理中の文字は、 >str[len-1-i] = TABLE[0]; TABLE[0]は、リストの最初 ('a')なので "abz" が "aba" になり 次の b の処理で "aba" が "aca" になることになります。 "zzz"の様な場合、既に述べたように下から桁上げしてきて "zaa"になり carryがある状態になります まず、既に述べたように "zaa" は、"aaa" に置き換えられます 最後の桁の場合、carryはココで処理するのでクリアするべきかも しれませんが、結局処理文字列も終わるのでループを明示的にここで抜ける必要も 既に述べたように、i がlen-1 が最後の処理文字位置ですから >if(i == len-1) のように調べられます。 ここですべきことは、リストの最初の文字を先頭に付け足すことです。 "aaa" を "aaaa"にします。 >bak=strdup(str); strdup は、文字列のコピーを作る関数です。 "aaa" を別の所に "aaa"ともう一つ作ります。 >wk[0]=TABLE[0];wk[1]='\0'; で "a"を作ります。 これは、ループの外に有った方がいいですね。^^; >strcpy(str,wk); strcpy は、文字列のコピーをする関数で str の領域に作った"a"をコピーします。 コピーすると元の文字列が失われてしまうので、 あらかじめstrdup で、別に保存したのでした。 >strcat(str, bak); strcat は、文字列の連結をする関数です。 結果 "a" "aaa" が連結されて "aaaa" になります >free(bak) 作業用にstrdupで作った領域を開放します。
その他の回答 (5)
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
>strlen(TABLE);ではいけないのでしょうか? 当然な疑問ですね。 "abc..."のような文字列の場合、strlenで文字列長が得られます。 ただ、普通のCコンパイラの場合、変数の初期化に関数や別の変数を使えません。(定数である必要があります) GCCの場合は、できたような気がしますが、それは、拡張機能です。 少しでも他のCコンパイラで動作させることを想定するなら(変数の宣言時の初期化に関数は)使わないほうがいいです。 普通の変数に代入することはもちろんできます。
お礼
お察しのとおりGCCでコンパイルしていました。 GCC限定という視野ということですね。 あと・・(sizeof(TABLE)/sizeof(char))-1; ということが根底にあるということを認識していないと今後のプログラミングに影響してくるかもしれませんね。 私の理解度が浅いかも知れませんね。 良い参考になりました。私なりに良い答えを教えてもらった気がします。 ありがとうございます。 また聞く機会があると思いますので・・またお願いします。orz
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
#4は、a,b,…z,aa には、ならないので仕様を満たしていません
補足
ふと思ったのですが TABLEから\0をとるとき sizeof(TABLE) -1; としないで strlen(TABLE);ではいけないのでしょうか? もし、使えたとして どっちを使うべきでしょうか?
- inetd
- ベストアンサー率23% (43/184)
すいません。先ほどのは細かい所を間違えていてコンパイルエラーが出ます。 ということで修正版です。 #include <stdio.h> /* n を a-z を使用する 26 進数に変換して buf からの col 桁の領域に入れる関数。 */ void int2az(int n, char *buf, int col) { int i; for (i = col - 1; i >= 0; i--) { if (n > 0) { buf[i] = (n % 26) + 'a'; n /= 26; } else { buf[i] = ' '; } } } int main() { char buf[4 + 1]; /* 4桁 + 最後の '\0' の領域。 */ int i; buf[4] = '\0'; /* 456976は26の4乗 */ for (i = 0; i < 456976; i++ ) { int2az(i, buf, 4); /* a-zを使用する26進表現の文字列に変換 */ printf("%s\n", buf); /* そのまま出力 */ } return 0; }
補足
26進数との考え、まったく思いつきませんでした。 ですが改良版 gcc test1.c -o test1 ./test1 > test1.txt として中を見てみたところ b c d e とaから始まっていません。
- inetd
- ベストアンサー率23% (43/184)
それは一桁にaからzまでの文字を使用して一桁で26段階の重みを表現できるので、a~zの文字を使用した26進数ということになります。 ならば、C言語の内部表現からこのa~zを使う26進数の文字列に変換してしまえば26進数での繰り上がり処理を考える必要がなくなります。 ということで、こんなのどうでしょう。 #include <stdio.h> /* n を a-z を使用する 26 進数に変換して buf からの col 桁の領域に入れる関数。 */ void int2az(int n, char *buf, int col) { int i; for (i = col - 1; i >= 0; i--) { if (n > 0) { buf[i] = (n % 26) + 'a'; n /= 26; } else { buf[i] = ' '; } } } int main() { char buf[4 + 1]; /* 4桁 + 最後の '\0' の領域。 */ int i; buf[4] = '\0'; /* 456976は26の4乗 */ for (i = 0; i < 456976; i++ ) { int2bz(i, buf, 4); /* a-zを使用する26進表現の文字列に変換 */ printf("%s\n", buf); /* そのまま出力 */ } retur 0; }
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
//試しに作ってみました #include <stdio.h> #include <stdlib.h> #include <string.h> //桁上がり文字リスト const char TABLE[]="abcdefghijklmnopqrstuvwxyz"; const unsigned TABLESIZE = sizeof(TABLE)/sizeof(char) -1;//\0を引く char *incStr(char *str){ int len, i; int carry=1; char wk[2]; len=strlen(str); for(i=0;i<len;i++){ if(carry){ //桁上がりがある if(str[len-1-i] == TABLE[TABLESIZE-1]){ //桁上がりすべき最後の文字である carry=1; //桁上がりする str[len-1-i] = TABLE[0]; //最初の文字にする if(i == len-1){ //最上位桁の場合、最初の文字を足す char *bak; bak=strdup(str); wk[0]=TABLE[0];wk[1]='\0'; strcpy(str,wk); strcat(str, bak); free(bak); } } else { carry=0; //単なるその桁のインクリメント str[len-1-i] = *(strchr(TABLE, str[len-1-i])+1); } } else { break; } } return str; } int main(void){ char str[6]="a"; do{ printf("%s\n", str); }while(strcmp(incStr(str),"aaaaa")); return 0; }
お礼
すいません・・さっきの質問は無視してください。 プログラムを走らせて見て確認しました。 ワカラナイ部分を抽出してみました。 if(carry){ //桁上がりがある if(str[len-1-i] == TABLE[TABLESIZE-1]){ //桁上がりすべき最後の文字である carry=1; //桁上がりする str[len-1-i] = TABLE[0]; //最初の文字にする if(i == len-1){ //最上位桁の場合、最初の文字を足す char *bak; bak=strdup(str); wk[0]=TABLE[0];wk[1]='\0'; strcpy(str,wk); strcat(str, bak); free(bak); } } else { carry=0; //単なるその桁のインクリメント str[len-1-i] = *(strchr(TABLE, str[len-1-i])+1); } ほとんどの処理が理解できていないと自覚しています。本当に馬鹿で申し訳ないですが 文章で具体的に説明お願いします。
補足
このプログラムは永遠に桁上がりするのでしょうか? まだ良くわかりません。。。
お礼
ディモールトォォォ!!な回答ありがとうございます。 一気に理解ができました。 すばらしいです。 もう本当にありがとうございます。