• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:文字列変換のプログラムについて)

C言語で文字列変換プログラムの実装で手こずっています。同じ結果しか表示されず、謎の文字もついてしまいます。どこを修正すればいい?

このQ&Aのポイント
  • C言語で文字列変換プログラムを作成していますが、うまく実装できず困っています。実行結果が謎の文字と同じものしか表示されず、入力ファイルの内容も正しく読み込まれません。
  • プログラムの流れを考えたところ、特に(2)の部分で手こずっています。文字列を正しく配列に保存できず、謎の文字が表示される原因になっています。
  • 入力ファイルが2行以上ある場合、1行目の結果しか出力されない問題もあります。プログラム全体の修正が必要なようです。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.5

#include<stdio.h> void main(void){ FILE *fin; FILE *fout; char buff[200]; char *p1,*p2; /*読み込み用ファイルを開く*/ fin = fopen("input.txt", "rt"); if( fin == NULL ){ printf( "File open error\n" ); return; } /*書き込み用ファイルを開く*/ fout = fopen("output.txt", "wt"); if( fout == NULL ){ printf( "File open error\n" ); return; } /*1行ずつ読み込む*/ /*読み込める間繰り返す*/ while(fgets(buff,200,fin) != NULL){ for (p1=buff;*p1;p1++) if (*p1 == '\t') { *p1++ = '\0'; break; } /*保存できているかの確認*/ printf("%s\t",buff); fprintf(fout,"%s\t",buff); for (p2=buff;*p2;p2+=2) { *(short *)p2 = *(short *)p2 + 41217; if ((p2[1] < 0) || (p2[1] == 127)) p2[1]++; } printf("%s\t%s\n",buff,p1); fprintf(fout,"%s\t%s\n",buff,p1); } fclose(fin); fclose(fout); }

その他の回答 (8)

回答No.9

>のところで、for文の終了条件がなぜ*p1となるのかが分かりません。(ポインタが苦手なもので・・・) C言語で「条件式において、非0は真、0は偽」です。 本当なら for (p1=buff;*p1 != '\0';p1++) { と書くべきですが、 条件式「*p1 != '\0'」は「0じゃないなら真、0なら偽」なので、条件式を「*p1」と書いても、結果は同じです。 > (結局if文でbreakするから何でもいいような気もしますが・・・) だめですよ。もしタブ文字が来なかったら、forループが終了しません。 文字列を「先頭から末尾までポインタでループする時」は、常套的に for (p1=buff;*p1;p1++) { と言う書き方をします。「決まり文句」みたいなモノです。 >また、 *p1++ = '\0'; について >>これはタブがあったら、タブの所に「文字列終端」を書き込んで「タブの次の位置をp1に覚える」と言う処理。 >と説明してくださっていますが、つまり、タブの部分に文字列終端(\0)を上書き→p1を次の位置(最初の音素)にする という処理を行っているということでしょうか? そうです。タブがあった場所に文字列終端を書き込んでから、p1を1つ進めています。 >2行目は文字コード分足してるのかな?と思うのですが、 > *(short *)p2 = *(short *)p2 + 41217; カタカナとひらがながの文字コードを「shortの数値」として扱った場合、両者の「数値の差」が「41217」なので、その分を足しています。 >3行目のif文と、 > if ((p2[1] < 0) || (p2[1] == 127)) p2[1]++; 差を足してカタカナにしたあと、シフトJISの文字コードの第2バイトが「127」か「負数」になった場合は、文字コードを1文字分「シフト(増加)」させる必要があります。 その「シフト(増加)分」を足しています。 これが「シフトJIS」が「シフトJIS」と呼ばれている所以(ゆえん)です。 >for文の繰り返し条件でなぜ+2するのかが分からないです。 >for (p2=buff;*p2;p2+=2) { シフトJISの漢字コードは「2バイトで1文字」なので、「1文字づつループする為」に「2バイトづつ進めている」のです。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.8

#6 の補足のところだけど.... for にしろ while にしろ, 与える条件は「終了条件」じゃなくて「継続条件」な. で, C の「条件」は「値が 0 なら偽, 0 でなければ真」だ. まあ, Perl なら use utf8; use Encode; while (<>) { $_ = Encode::decode(ファイルの文字コード, $_); chomp; my ($word, $phonic) = split /\t/; my $w = $word =~ tr/ひらがなたち/カタカナたち/r; print Encode::encode(文字コード, join("\t", $word, $w, $phonic)), "\n"; } くらい (スクリプトの文字コードは UTF-8) でいいと思うんだけど.

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.7

>>printf("%d", '\t'); >>してみては? >結果「9」になりました。 >ASCIIの制御コードでしょうか・・・。 …タブのASCIIコードは9でしたな。 8だったのは…ゴミデータ?? >>した後のbuffの中身をダンプしてみて下さい。 >FFFFFF82FFFFFFA0FFFFFF82FFFFFFA2FFFFFF82FFFFFFA4FFFFFF82FFFFFFA6FFFFFF82FFFFFFA809 >61206920752065206F0000000048FFFFFFAA220000000000FFFFFFC0FFFFFFFF11FFFFFF8001000000 for(i=0;i<sizeof(buff);i++) printf("%02X ", (unsigned int)buff[i]); とすべきでしたか……。 >16進数の何かでしょうか・・・? 読み込んだ文字列のコードです。 Shift-JISですかね。 途中に00が出力されている場所が'\0'になります。 # それ以降のデータは「文字列」としては無視される。

sou-e9
質問者

補足

ありがとうございます。 ダンプした結果について >読み込んだ文字列のコードです。 >Shift-JISですかね。 > 途中に00が出力されている場所が'\0'になります。 ># それ以降のデータは「文字列」としては無視される。 buffとwordのダンプの結果を見ると、「あいうえお」は両方ちゃんと入っているみたいです。 ということは、wordの「お」(A8)から00までの間の 2CFFFFFFFDFFFFFFFE07 が文字化けの原因になっているということでしょうかね。 うーん、色々と謎です・・・。

回答No.6

訂正と解説。 printf("%s\t%s\n",buff,p1); fprintf(fout,"%s\t%s\n",buff,p1); の2行は printf("%s\t%s",buff,p1); fprintf(fout,"%s\t%s",buff,p1); に変更して下さい。 for (p1=buff;*p1;p1++) if (*p1 == '\t') { *p1++ = '\0'; break; } これはタブがあったら、タブの所に「文字列終端」を書き込んで「タブの次の位置をp1に覚える」と言う処理。 こうすると printf("%s\t",buff); fprintf(fout,"%s\t",buff); で「あいうえお<タブ>」を出力できる。 buffは あいうえお + EOS + a i u e o + 改行 + EOS になっていて、p1は「a」の位置を示している。 for (p2=buff;*p2;p2+=2) { *(short *)p2 = *(short *)p2 + 41217; if ((p2[1] < 0) || (p2[1] == 127)) p2[1]++; } は「ひらがなをカタカナに直に変換」している(文字コードは「シフトJIS」を想定している) 元のひらがなは、既にprintf、fprintfで出力済みなので「ひらがなを直接書き換えてカタカナにしてしまう」事が可能なのだ。 その後で printf("%s\t%s",buff,p1); fprintf(fout,"%s\t%s",buff,p1); により「カタカナ<タブ>音素列<改行>」を出力している(p1の末尾には改行が入っているので、明示的な改行はしない) その結果、 あいうえお a i u e o かきくけこ ka ki ku ke ko さしすせそ sa si su se so たちつてと ta ti tu te to なにぬねの na ni nu ne no はひふへほ ha hi hu he ho まみむめも ma mi mu me mo やゆよ ya yu yo わをん wa wo n の入力が あいうえお アイウエオ a i u e o かきくけこ カキクケコ ka ki ku ke ko さしすせそ サシスセソ sa si su se so たちつてと タチツテト ta ti tu te to なにぬねの ナニヌネノ na ni nu ne no はひふへほ ハヒフヘホ ha hi hu he ho まみむめも マミムメモ ma mi mu me mo やゆよ ヤユヨ ya yu yo わをん ワヲン wa wo n となります。

sou-e9
質問者

補足

回答ありがとうございます。 プログラムまで作っていただき、申し訳ないです。 ポインタを使ってやるということは考えても無かったです・・・。 作っていただいたプログラムを読ませていただいたのですが、良く分からない所があるので、お時間があるときで構わないので教えていただきたいです。 (1) for (p1=buff;*p1;p1++) if (*p1 == '\t') { *p1++ = '\0'; break; } のところで、for文の終了条件がなぜ*p1となるのかが分かりません。(ポインタが苦手なもので・・・) *p1ということはbuffで保持している文字列のどれかであると思うのですが、なぜ*p1だけでいいのかが分からないです。 (結局if文でbreakするから何でもいいような気もしますが・・・) また、 *p1++ = '\0'; について >これはタブがあったら、タブの所に「文字列終端」を書き込んで「タブの次の位置をp1に覚える」と言う処理。 と説明してくださっていますが、つまり、タブの部分に文字列終端(\0)を上書き→p1を次の位置(最初の音素)にする という処理を行っているということでしょうか? (2)ひらがなからカタカナへの変換部分 for (p2=buff;*p2;p2+=2) { *(short *)p2 = *(short *)p2 + 41217; if ((p2[1] < 0) || (p2[1] == 127)) p2[1]++; } を詳しく説明していただきたいです。 2行目は文字コード分足してるのかな?と思うのですが、3行目のif文と、for文の繰り返し条件でなぜ+2するのかが分からないです。 長々となってしまい申し訳ないです。 どうぞよろしくお願いします。

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.4

>この「8」が意味していることはいったい何なのでしょうか・・・。 printf("%d", '\t'); してみては?

sou-e9
質問者

補足

ありがとうございます。 >printf("%d", '\t'); >してみては? 結果「9」になりました。 ASCIIの制御コードでしょうか・・・。

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.3

アクセス違反でふっとんでもいいコード…ですかねぇ。 >てことはもしや取り出した1行は >あいうえお(文字列)+$0+Tab+a i u e o(文字列)+$0+(文末) >という構成になっているのでしょうか? fgets(buff,200,fin) した後のbuffの中身をダンプしてみて下さい。 for(i=0;i<sizeof(buff);i++) printf("%02X ", buff[i]); みたいな感じで。 で…… >/*保存できているかの確認*/ >printf("%s",word); の時点で'\0'で終端しているか確認して下さい。 # ダンプについては上記のような処理で。 >/*タブ文字がくるまで拾う*/ が開始される時点でのiの値にも注意…でしょうね。 で…… {と}の対応が取れていますか? # 現状掲示されているコードだと、1行目読み込んだ後ファイルクローズしています。 # ので、その次の読み込みで吹っ飛びかねませんが。 # というかmain()閉じていないからコンパイルできない。

sou-e9
質問者

補足

ありがとうございます。 >fgets(buff,200,fin) >した後のbuffの中身をダンプしてみて下さい。 FFFFFF82FFFFFFA0FFFFFF82FFFFFFA2FFFFFF82FFFFFFA4FFFFFF82FFFFFFA6FFFFFF82FFFFFFA809 61206920752065206F0000000048FFFFFFAA220000000000FFFFFFC0FFFFFFFF11FFFFFF8001000000 0300000000000000FFFFFFC0FFFFFFFF11FFFFFF800000000040FFFFFFD4220000000000FFFFFFE0FF FFFFC42BFFFFFF800100000078FFFFFFAA220000000000702717FFFFFF800100000001000000726F77 00702717FFFFFF8001000000502317FFFFFF800100000000FFFFFFCE220000000000FFFFFFC0FFFFFF C42BFFFFFF800100000041534349490000002A2116FFFFFF80010000000000000000000000FFFFFFFE FFFFFFA106FFFFFF800100000000FFFFFFCE22000000000030FFFFFFAB22000000000068072EFFFFFF 8001000000FFFFFFF0FFFFFFCC220000000000FFFFFF90FFFFFFAB220000000000 となりました。 16進数の何かでしょうか・・・? また、タブ文字までをwordに保存して、その後wordの中身を上と同様にダンプすると FFFFFF82FFFFFFA0FFFFFF82FFFFFFA2FFFFFF82FFFFFFA4FFFFFF82FFFFFFA6FFFFFF82FFFFFFA82C FFFFFFFDFFFFFFFE0700000000000000000000000000000000000070FFFFFFAA220000000000FFFFFF F3FFFFFF9E0BFFFFFF800100000050FFFFFFAA22000000000004FFFFFFE024FFFFFF80010000000200 0000000000000100000000000000FFFFFFA0FFFFFFAA2200000000000000000000000000FFFFFF80FF FFFFAA22000000000004FFFFFFE024FFFFFF800100000000FFFFFFAA2200000000000B0D07FFFFFF80 0100000060FFFFFF9F1CFFFFFF80010000000E6019FFFFFF8001000000FFFFFF86FFFFFFAA22000000 0000616D16FFFFFF800100000070FFFFFFAA220000000000FFFFFFBC1B16FFFFFF8001000000702717 FFFFFF8001000000FFFFFF80FFFFFFAA2200000000000100000001000000 となりました。 >で…… >{と}の対応が取れていますか? すみません、}が1つ足りなかったです。

  • akubi_m
  • ベストアンサー率22% (12/54)
回答No.2

fgets()する前のbufとword[i] = buff[i];する前のwordをprintf()してみてください。 何か分かりませんか?

sou-e9
質問者

補足

ありがとうございます。 printfすると、buffのほうは文字化けでちょっとよくわからなかったのですが、wordのほうは8になりました。 この「8」が意味していることはいったい何なのでしょうか・・・。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

C の「お約束」です: 「文字列」の最後には, 何がありますか? もちろん最後に書かれているように Perl などで作る方が「はるかに」楽.

sou-e9
質問者

お礼

すみません。$0でなく\0です。

sou-e9
質問者

補足

ありがとうございます。 文字列の最後はnull文字($0)がつくんですよね。 てことはもしや取り出した1行は あいうえお(文字列)+$0+Tab+a i u e o(文字列)+$0+(文末) という構成になっているのでしょうか? やはりPerlのほうが楽ですよね・・・。 Perlで作ることも考えていこうと思います。

関連するQ&A