アプリ版:「スタンプのみでお礼する」機能のリリースについて

こんにちは。
C言語で以下のようなプログラムを作りたいと考えているのですが、うまく実装できず困っています。

入力ファイルには、【単語(ひらがな)+Tab+その読み(音素列)】が書かれているとします。
例:
あいうえお(Tab)a i u e o
かきくけこ(Tab)ka ki ku ke ko
・・・
(音素は半角空白で分けられているものとします)
このファイルを読み込んで、【単語(ひらがな)+Tab+単語(カタカナ)+Tab+その読み(音素列)】を別のファイルに書き込む 
例:
あいうえお(Tab)アイウエオ(Tab)a i u e o
かきくけこ(Tab)カキクケコ(Tab)ka ki ku ke ko
・・・
というプログラムを作りたいと考えています。

アルゴリズムとして、
(1) ファイルから1行読み込む
(2) Tab文字がくるまでの文字列を配列に保存(例でいう「あいうえお」の部分です)
(3) Tab文字の次の文字から行末までの文字列を配列に保存(例でいう「a i u e o」の部分です)
(4) (2)の1文字ずつをカタカナに変換(ASCIIコードの変換で可能?)
(5) (2)(3)(4)を用いて出力用ファイルに書きこむ
という流れを考えたのですが、(2)の部分でまず手こずっています。

#include<stdio.h>
main(){

FILE *fin;
FILE *fout;
char buff[200];
char word[200];
int i=0;

/*読み込み用ファイルを開く*/
fin = fopen("input.txt", "r");
if( fin == NULL ){
printf( "File open error\n" );
return;
}

/*書き込み用ファイルを開く*/
fout = fopen("output.txt", "w");
if( fout == NULL ){
printf( "File open error\n" );
return;
}

/*1行ずつ読み込む*/
/*読み込める間繰り返す*/
while(fgets(buff,200,fin) != NULL){
/*タブ文字がくるまで拾う*/
while(buff[i] != '\t'){
/*文字列を配列wordに保存*/
word[i] = buff[i];
i++;
}
/*保存できているかの確認*/
printf("%s",word);
fclose(fin);
fclose(fout);

}

実行後、wordの中身が表示されるのですが、
あいうえお,(文字化け)(文字化け)
というように、【,(文字化け)(文字化け)】という謎の文字がついてしまいます。
また、入力ファイルの中身が2行以上でも、
あいうえお,(文字化け)(文字化け) あいうえお,(文字化け)(文字化け)・・・
というように、1行目の結果しか出てこないです。


どこを直せばいいのでしょうか?
(というかそもそもCよりPerlとかで作ったほうがもっと楽なのかもしれないですが・・・)
教えて頂けると助かります。よろしくお願いします。

A 回答 (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バイトづつ進めている」のです。
    • good
    • 0

#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) でいいと思うんだけど.
    • good
    • 0

>>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'になります。
# それ以降のデータは「文字列」としては無視される。

この回答への補足

ありがとうございます。

ダンプした結果について
>読み込んだ文字列のコードです。
>Shift-JISですかね。
> 途中に00が出力されている場所が'\0'になります。
># それ以降のデータは「文字列」としては無視される。

buffとwordのダンプの結果を見ると、「あいうえお」は両方ちゃんと入っているみたいです。
ということは、wordの「お」(A8)から00までの間の
2CFFFFFFFDFFFFFFFE07
が文字化けの原因になっているということでしょうかね。
うーん、色々と謎です・・・。

補足日時:2014/10/24 21:54
    • good
    • 0

訂正と解説。



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

となります。

この回答への補足

回答ありがとうございます。
プログラムまで作っていただき、申し訳ないです。

ポインタを使ってやるということは考えても無かったです・・・。
作っていただいたプログラムを読ませていただいたのですが、良く分からない所があるので、お時間があるときで構わないので教えていただきたいです。

(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するのかが分からないです。


長々となってしまい申し訳ないです。
どうぞよろしくお願いします。

補足日時:2014/10/24 21:20
    • good
    • 0

#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);

}
    • good
    • 0

>この「8」が意味していることはいったい何なのでしょうか・・・。



printf("%d", '\t');
してみては?

この回答への補足

ありがとうございます。

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

補足日時:2014/10/24 17:42
    • good
    • 0

アクセス違反でふっとんでもいいコード…ですかねぇ。



>てことはもしや取り出した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()閉じていないからコンパイルできない。

この回答への補足

ありがとうございます。

>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つ足りなかったです。

補足日時:2014/10/24 17:30
    • good
    • 0

fgets()する前のbufとword[i] = buff[i];する前のwordをprintf()してみてください。


何か分かりませんか?

この回答への補足

ありがとうございます。

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

補足日時:2014/10/24 16:53
    • good
    • 0

C の「お約束」です: 「文字列」の最後には, 何がありますか?



もちろん最後に書かれているように Perl などで作る方が「はるかに」楽.

この回答への補足

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

やはりPerlのほうが楽ですよね・・・。
Perlで作ることも考えていこうと思います。

補足日時:2014/10/24 16:49
    • good
    • 0
この回答へのお礼

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

お礼日時:2014/10/24 16:57

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!