14歳の自分に衝撃の事実を告げてください

fgetsでファイルを一行ずつ読み込みたいのですが、二行目以降が文字化けしてしまいます。

*******
ソース
*******
#include <windows.h>
#include <stdio.h>

FILE *fp;

if ((fp = fopen("textlist.txt", "r")) == NULL){
MessageBox(NULL, TEXT("ファイルを開けません"), NULL, NULL);
exit (1);
}

while (1) {
TCHAR buf[128] = {0};
if (fgets(buf, sizeof(buf), fp) == NULL) break;
MessageBox(NULL,buf,NULL,NULL);
}

fclose(fp);

*****
textlist.txt
*****
あいうえお
かきくけこ
さしすせそ


メッセージボックスの一回目は正しく"あいうえお"と表示されますが、二回目・三回目は文字化けしています。
最終的に一行ずつ分けて配列に入れたいので、fgetsで出来たらと思っています。

よろしくお願いします。

A 回答 (6件)

わかりました。


ちょっと事情があって VC7.1(2003)で試したのですが
2005でも同じだと思います。

MSDNによると
fgets、fgetws (CRT)
http://msdn2.microsoft.com/ja-jp/library/c37dh6k …

fgetws は、ワイド文字引数 str の読み出しを行い、stream がテキスト モードで開かれた場合はマルチバイト文字列として、バイナリ モードで開かれた場合はワイド文字列として読み出します。

とありますので、テキストをUnicodeで書いていたとしても、


if ((fp = fopen("textlist.txt", "r")) == NULL){

のようにテキストモードでオープンしてしまうと、
_fgetts(実体は fgetws)で読み込んだときに自動的に
ShiftJISに変換されてしまいます。
#実際はもうちょっと手が入るようですが

fopen(, "r")/fgets の組み合わせで2行目以降が
化けるのは前回の説明でいいとして、
結局のところ、
"rb" で fopen(もしくは _tfopen)を呼び出し、
fgetws(か _fgetts)で読み込んで
MessageBoxWで表示
のようにしないといけないようです。

#define _UNICODE
#define UNICODE

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

#pragma comment(lib, "user32.lib")

int
main()
{
FILE *fp;

if ((fp = _tfopen(TEXT("textlist.txt"), TEXT("rb"))) == NULL){
MessageBox(NULL, TEXT("ファイルを開けません"), NULL, 0);
exit (1);
}

while (1) {
TCHAR buf[128] = {0};
if (_fgetts(buf, sizeof(buf), fp) == NULL)
break;
MessageBox(NULL, buf, NULL, 0);
}

fclose(fp);

return 0;
}

こんな感じのでとりあえず動いているようです。
#ただし あいうえおの前にゴミが出る

テキストファイルの文字コードはメモ帳の Unicode でいいです。
    • good
    • 0
この回答へのお礼

できました!ありがとうございます。
バージョン違いからか、こちらでは前にゴミは出ませんでした^^
何度も回答いただいて、ありがとうございました。

お礼日時:2007/07/24 13:08

すみません。


見落としと勘違いがありました。

> if (fgets(buf, sizeof(buf), fp) == NULL) break;

のように fgets を使っていますが、これではUnicodeが使われている
テキストファイルを正しく読むことができません。
というのも、行末の改行は(WindowsではLittle EndianのUnicodeなので)
0x0d 0x00 0x0a 0x00 のようになりますが、fgetsはこれがUnicodeであるとは
みなしていないので、0x0a まで読み進めたところで行末を見つけたと
判定してしまいます。
当然次の行は 0x00から始めますので、見事に位置がずれてしまうというわけです。

ファイルの先頭行はこういうずれは決して起きないので、正しく表示されていた。
というわけです。

ということで fgets を _fgetts に置き換えてやればよいのではないでしょうか?

fgets、fgetws (CRT)
http://msdn2.microsoft.com/ja-jp/library/c37dh6k …

この回答への補足

_fgettsにしましたが、最初から文字化けしてしまいました。
テキストファイルをBigEndianにしても、文字化けしました・・

補足日時:2007/07/23 14:31
    • good
    • 0

テキストをShiftJISで書いたものにして、


マルチバイト文字環境でコンパイルすると文字化けは起きませんでした。

で、
> テキストファイルはXPのメモ帳でUnicodeで保存。

ということなので、メモ帳で同じようなテキストを作って確認したところ、

00000000: FF FE 42 30 44 30 46 30 48 30 4A 30 0D 00 0A 00 .~B0D0F0H0J0....
00000010: 4B 30 4D 30 4F 30 51 30 53 30 0D 00 0A 00 55 30 K0M0O0Q0S0....U0
00000020: 57 30 59 30 5B 30 5D 30 0D 00 0A 00 W0Y0[0]0....

のようになっています。
多分、MessgeBoxWに渡すテキストには BOMが必要なのに、
ファイルからとってきたテキストでは
ファイルの先頭にしかBOMがないので正しい
Unicodeによる適すとしてみてくれなくて
結果として化けたんじゃないですかね。

メモ帳で、Unicode(Big Endian) でセーブしたテキストだと
どうなりますか?

この回答への補足

ありがとうございます。
今手元に環境がないため、今度試したときまた報告させていただきます。

補足日時:2007/07/20 18:12
    • good
    • 0

★コンパイラは何ですか?


・もしも VC++2005 ならばオプション設定で『マルチバイト文字セットを使用する』に
 変更してみて下さい。多分、設定のせいです。
 理由としてはプログラムが Unicode 文字を扱うように作られていた場合に fgets()
 でファイルから読み込むとマルチバイト文字(シフトJIS文字)のために表示がおかしく
 なると思います。これはテキストファイルが Unicode 文字ではないからです。
・設定を変更するには構造プロパティを開き→『全般』→『文字セット』の項目を
 『マルチ バイト文字セットを使用する』にします。
 でもちょっと気になるのが
>メッセージボックスの一回目は正しく"あいうえお"と表示されますが、
>二回目・三回目は文字化けしています。
 ↑
 となっている点です。最初の一回目だけは正しく表示されるのか?不思議?
・あと MessageBox() の第4引数は NULL ではなく MB_OK 定数を使いましょう。

余談:
・fopen() 関数のオープン文字列で『rt』は『rb』に対してテキストモードを指定する
 意味ですが昔、聞いた話ではどこかの処理系の拡張指定だったと思います。
 なので普段テキストは『r』『w』『a』の指定で良いです。
 バイナリは『b』を付けた『rb』『wb』『ab』と指定します。
・以上。

この回答への補足

VC++2005です。マルチバイト文字にすると一回目から文字化けしました。

情報が不足で申し訳ありません。
WindowsXP
Microsoft Visual Studio 2005 Academic Edition
テキストファイルはXPのメモ帳でUnicodeで保存。
プロジェクトオプション "Unicode文字セットを使用する"
ソースファイルの拡張子はcppでなくcにしています。


# fopenはrtにしても変わりませんでした。
# 一回目で文字化けすれば諦めてfgets以外の方法を探すのですが、
# 中途半端に上手くいくばっかりに困ってます・・

補足日時:2007/07/20 14:40
    • good
    • 0

他人の回答にケチを付けるのは気が引けますし、禁止事項にも抵触するのでしょうが、放置しているわけにはいかないので指摘します。



> テキストファイルを読み込む場合は「"r"」ではなく「"rt"」で。

上記は嘘です。

> 「"r"」を指定した場合は「処理系依存」なので、もしバイナリで開かれた場合は改行文字が正しく処理できずfgetsは「動作結果は未定義」となります。

そんなことはありません。
むしろ、"rt"を指定した場合の動作が未定義になります。

> ※ライブラリ仕様の「動作結果は未定義」とは「バグるから何が起きるか予想できない」と言う意味です。

何が起きるか予想できないのはある意味その通りですが、必ずしも「バグる」わけではありません。
今回の件に関しては、標準規格でも、("rt"のような規定外の文字列を使用した場合には)「動作は未定義とする」としつつも、処理系は残りの文字の並び(今回でいえば"t")を無視してよいなどの具体例を挙げています。
    • good
    • 0

>fopen("textlist.txt", "r")


テキストファイルを読み込む場合は「"r"」ではなく「"rt"」で。

fopen("textlist.txt", "rt")

「"r"」を指定した場合は「処理系依存」なので、もしバイナリで開かれた場合は改行文字が正しく処理できずfgetsは「動作結果は未定義」となります。

※ライブラリ仕様の「動作結果は未定義」とは「バグるから何が起きるか予想できない」と言う意味です。
    • good
    • 0

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

このQ&Aを見た人はこんなQ&Aも見ています


おすすめ情報