こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

winsock recvでの文字化け

Winsockによりサーバ名、ポート番号、URLを指定するとそのソースファイルをテキスト出力するプログラミングを作成しています。
文字エンコーディングがShift_JISであるページは日本語も正しく表示させることができたのですが、それ以外の文字エンコーディング(EUC-JP、Unicode(UTF-8))のページでは日本語が文字化けしてしまいうまく出力できません。
デバックでrecv関数で文字を取得した際の変数szStrRcvを確認したところ、取得した時点で既に文字化けしているようです。
以下に問題部のソースを示します。
開発環境:WindowsXP Visualstudio.NET2003 VisualC++
WSADATA wsaData;
LPHOSTENT lpHost;
SOCKET s;
int nRtn;
SOCKADDR_IN sockadd;
_TCHAR szStrRcv[1024];
char szURL[1024]//URL
char szStr[256], szYN[4];
u_short sock_port;
unsigned int addr;


fp = fopen(filename, "a");
sock_port = (u_short)atoi(port);

s = socket(PF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) {
//perror("ソケットをオープンできません\n");
//WSACleanup();
//return -2;
}
lpHost = gethostbyname(host);
if (lpHost == NULL) {
addr = inet_addr(host);
lpHost = gethostbyaddr((char *)&addr, 4, AF_INET);
//wsprintf(szStr, "%sが見つかりません\n", host);
//perror(szStr);
//WSACleanup();
//return -3;
}
memset(&sockadd, 0, sizeof(sockadd));
sockadd.sin_family = AF_INET;
sockadd.sin_port = htons(sock_port);
sockadd.sin_addr = *((LPIN_ADDR)*lpHost->h_addr_list);
if (connect(s, (PSOCKADDR)&sockadd, sizeof(sockadd)) != 0) {
//perror("サーバーソケットに接続失敗\n");
closesocket(s);
}
wsprintf(szStr, "GET %s\n", szURL);//szURLには正常にURLが入っている
nRtn = send(s, szStr, (int)strlen(szStr), 0);

while(1) {
memset(szStrRcv, '\0', sizeof(szStrRcv));
///////////////////////////問題の部分/////////////////////////////
nRtn = recv(s, szStrRcv, (int)sizeof(szStrRcv) - 1, 0);
_ftprintf(fp, _T("%s\n"), szStrRcv);//HTMLソースを.txtに書き込む
//////////この時点でszStrRcvには日本語が文字化けした内容が格納されている///////////////
if (nRtn == 0)
break;
if (nRtn == SOCKET_ERROR) {
perror("recvエラーです\n");
break;
}
}

closesocket(s);
WSACleanup();
fclose(fp);


何か対策がありましたらご教授をお願いいたします。

投稿日時 - 2009-01-19 10:18:49

QNo.4642426

すぐに回答ほしいです

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

>_ftprintf(fp, _T("%s\n"), szStrRcv); //HTMLソースを.txtに書き込む
>//////////この時点でszStrRcvには日本語が文字化けした内容が格納されている///////////////

文字化けも何も、EUCやUTF-8でエンコードされたページは「EUCやUTF-8のまま」送られて来るので、そのまま表示しても文字化けしてて当たり前。

ちゃんと表示したいなら、何のコードで送られて来たか見て、自分で「EUC⇒Shift_JIS」や「UTF-8⇒Shift_JIS」に変換してあげないとならない。

あと
_ftprintf(fp, _T("%s\n"), szStrRcv);
はやってはダメ。

1回の受信は「多バイト文字の、文字の途中」で途切れる可能性があるので、勝手に改行を足してはいけない。

例えば、漢字の「亜」は、UTF-8で「E4 BA 9C」の3バイトだが、1回のrecvで「E4 BA」までしか来ず、次のrecvで続きの「9C … …」が来る事もある。

それを
_ftprintf(fp, _T("%s\n"), szStrRcv);
などと「バカな事」をしてしまうと、受信テキストは「E4 BA 改行 9C」となって、漢字の「亜」の途中に改行コードを入れてしまう。

これでは「文字化けして当たり前」だろう。

てゆか、Shift_JISでも「亜」は「88 9F」の2バイトなんだから、勝手に改行を足してたら「88 改行 9F」って感じで、文字化けしてる筈。今まで文字化けしてたのに気が付かなかっただけで。

投稿日時 - 2009-01-19 11:19:09

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.3

もう1つ。

>HTMLソースを.txtに書き込む

もし、UTF-8のコードのまま.txtに保存し、それをメモ帳で開いて正しく表示したいのなら、ファイルの先頭に「Byte Order Mark(BOM)」が無いといけない。

マイクロソフトの仕様では、UTF-8で保存されたテキストファイルの先頭には「EF BB BF」がある事になっている。

なので、ファイルの先頭にこの3バイトが無いと、メモ帳で開いた瞬間にUTF-8とは認識されず「文字がグチャグチャ」に文字化ける。

なお、メモ帳はEUCに対応してないので、EUCは必ず文字化ける。

投稿日時 - 2009-01-19 11:32:44

お礼

わかりました。
貴方様の意見を参考にもう少し自分で調べてみます。
現段階でなんとか文字列をANSIに変換してtxtに保存できるようになりました。

ご教授ありがとうございました。

投稿日時 - 2009-01-19 15:38:19

ANo.1

1.文字化けはどこで確認しましたか?
単純にEUCやUTF-8を認識できないプログラムで見ているだけではないですか?
VisualStudioのウォッチ画面などではShift-JIS以外の文字列を表示することはできないはずです。

2.全てのデータを受信していますか?
recvでは要求ファイルの全サイズを一度に読み込むわけではありません。
通信環境により、例えば10kのファイルだったとしても1バイトしか読み込まない可能性もありえます。
以下のようにやるのが定番だと思います。

char buf[10*1024] = {0}; // 十分なサイズを用意しておく事
int offset = 0;
int r = 1
while(r > 0)
{
// ネットワークからデータを読み込む。
// 返り値が正の数だったら、まだデータが残っている可能性がある
// 0だったら全てのデータを読み込んでサーバから切断された
// 負数だったらエラーが発生した
r = recv(s, &buf[offset], sizeof(buf)-offset-1, 0);
}

投稿日時 - 2009-01-19 10:48:15

お礼

わかりました。
貴方様の意見を参考にもう少し自分で調べてみます。

ご教授ありがとうございました。

投稿日時 - 2009-01-19 15:37:22

あなたにオススメの質問