@FubiraiHan

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

C++: バイナリのデコード時に文字化けが起きる

Q&A

Closed

今winsock2を使ってクライアントからサーバーにファイルを送るプログラムを書いています。
ファイルを送る機能の試作として以下のようなプログラムを書きました。

test.cpp
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>

#define MESSAGE_LENGTH 10
int main()
{	
	char buffer[MESSAGE_LENGTH];
	std::string message_gather;
	std::ifstream file("test.cpp", std::ios_base::ate, std::ios_base::binary);
    std::string str;

	std::streamsize end_pos = file.tellg();
	file.seekg(0, std::ios::beg);

	while (file.read(buffer, sizeof(buffer))) {
		str += std::string(buffer);
	}
	if (end_pos - file.tellg() > 0) {
		memset(buffer, '\0', sizeof(buffer));
		file.read(buffer, sizeof(char) * (end_pos - file.tellg()));
		str += std::string(buffer);

	}
	std::cout << str << std::endl;
	
}

機能としてはファイルの中身をMESSAGE_LENGHTで分割してパケットに乗せて送信し、それを受け取り手が合体してファイルを復元する、といったことをしたいと思っています。このコードでは送信する部分を省いて、ファイルの分割→再結合を行っています。

出力は以下のとおりです。

#include <フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォiostream>
フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ#include <フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォsstream>
#フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォinclude <fフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォstream>
#iフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォnclude <veフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォctor>

#deフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォfine MESSAフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォGE_LENGTH フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ10
int maiフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォn()
{
        chaフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォr buffer[Mフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォESSAGE_LENフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォGTH];
        stdフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ::string mフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォessage_gatフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォher;
        std:フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ:ifstream フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォfile("testフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ.cpp", stdフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ::ios_baseフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ::ate, stdフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ::ios_baseフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ::binary);フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ
        std::strフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォing str;

フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ     std::streフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォamsize endフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ_pos = filフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォe.tellg();フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ
        file.seeフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォkg(0, std:フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ:ios::beg)フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ;

        while フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ(file.readフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ(buffer, sフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォizeof(buffフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォer))) {
                フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォstr += stdフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ::string(bフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォuffer);
        }フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ
        if (fileフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ.gcount() フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ> 0) {
                mフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォemset(buffフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォer, '\0', フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォsizeof(bufフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォfer));
                fフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォile.read(bフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォuffer, sizフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォeof(char) フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ* (end_posフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ - file.teフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォllg()));
        フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ     str += stフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォd::string(フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォbuffer);

フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ     }
        std::cフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォout << strフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ << std::eフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフタ稀モォ

デコード自体はできているようですが、不要な部分(フフフ...の部分)までデコードしてしまっている?様です。

質問ですが
1:なぜフフフ...の部分が出力に表れているのか、
2:whileループ部分で読み取り切れなかった、MESSAGE_LENGTHよりも短い残りを読み取る部分(ifの部分)がなぜ動いていないのか
3:そもそもファイルを分割して送信→再結合するやり方としてもっといい方法があるか
お聞きしたいです。

0 likes

2Answer

バイナリ文字列をは、string型でないので、str += で連結してもうまくいかないです。
std::string のコンストラクタを使用すればよいのでは?

	while (file.read(buffer, sizeof(buffer))) {
-		str += std::string(buffer);
+		std::string strBuff( buffer, file.gcount());
+		str += std::string(strBuff);
	}
-	if (end_pos - file.tellg() > 0) {
-		memset(buffer, '\0', sizeof(buffer));
-		file.read(buffer, sizeof(char) * (end_pos - file.tellg()));
-		str += std::string(buffer);
+	std::streamsize left = file.gcount();
+	if (left > 0) {
+		std::string strBuff( buffer, left);
+		str += std::string(strBuff)
	}

または

-	while (file.read(buffer, sizeof(buffer))) {
+	while( !file.eof() ){
-		str += std::string(buffer);
+		file.read(buffer, sizeof(buffer));
+		std::string strBuff( buffer, file.gcount());
+		str += std::string(strBuff);
	}
-	if (end_pos - file.tellg() > 0) {
-		memset(buffer, '\0', sizeof(buffer));
-		file.read(buffer, sizeof(char) * (end_pos - file.tellg()));
-		str += std::string(buffer);
-
-	}

でどうでしょうか?

1:なぜフフフ...の部分が出力に表れているのか、

正しく連結されていないからです。(バイナリ文字列をは、string型でないので・・・)

2:whileループ部分で読み取り切れなかった、MESSAGE_LENGTHよりも短い残りを読み取る部分(ifの部分)がなぜ動いていないのか

こちらで見る限りでは、ifの部分は end_posが0、file.tellg()が-1になっていて動作していました。
しかし、データを読み切ってるので Readしたら0文字です。
しかし、ここでもバイナリ文字列を string型で連結してるので、うまく連結されないでしょう。

3:そもそもファイルを分割して送信→再結合するやり方としてもっといい方法があるか
お聞きしたいです。

方法としては、分割 →送信 受信 →再結合 は変わらないと思います。コード化に際してをおっしゃているのであれば、
再結合のコードが

		str += std::string(buffer);

なのであれば、受け取り手では「わざわざstring型で結合しないで受信したデータをファイルに書き込んじゃえば良いのに」かなって思います。

0Like

Comments

  1. @FubiraiHan

    Questioner

    回答ありがとうございます。
    二点質問なのですが、
    1: readのリファレンスには

    また、n文字より少ない段階でEOFに達した場合を入力失敗として扱う

    とあったのですが、これはn文字より少ない段階でEOFに達した場合は
     読み取る(bufferに移す)処理をせずに失敗を返す
    のではなく
     読み取った後に失敗として返す
    ということでしょうか
    2:

    str += std::string(strBuff);
    

    について、strBuffは定義よりstring型だと思っていますが、strに加える際に改めてstringであるとキャスト?するのはどういった理由からでしょうか。

  2. 1: readのリファレンスには

    また、n文字より少ない段階でEOFに達した場合を入力失敗として扱う

    とあったのですが、これはn文字より少ない段階でEOFに達した場合は
    _ 読み取る(bufferに移す)処理をせずに失敗を返す
    のではなく
     読み取った後に失敗として返す
    ということでしょうか

    そう思います。

    について、strBuffは定義よりstring型だと思っていますが、strに加える際に改めてstringであるとキャスト?するのはどういった理由からでしょうか。

    基本的にstring型は文字列の先頭アドレスと長さで管理されてると思います。
    bufferはchar型です。
    char型では、文字列は長さではなく'\0'の出現位置までを文字列として扱います。

    file.read(buffer, sizeof(buffer)
    

    で読み込んだbuffer変数を、文字列として扱うには読み込んだbufferの最後に'\0'をつけないといけないのですが、質問者殿のコードにはその処理がありませんので

    std::string strBuff( buffer, file.gcount());
    

    で文字列の長さ情報を与えてstring型にしてます。
    これなしであれば、このようになると思います。

    -	char buffer[MESSAGE_LENGTH];
    +	char buffer[MESSAGE_LENGTH+1];
        ・
      ・
      ・
    -	while (file.read(buffer, sizeof(buffer))) {
    +	while (file.read(buffer, MESSAGE_LENGTH)) {
    +		buffer[MESSAGE_LENGTH] = '\0';
    		str += std::string(buffer);
    	}
    -	if (end_pos - file.tellg() > 0) {
    +	std::streamsize left = file.gcount();
    +	if (left > 0) {
    -		memset(buffer, '\0', sizeof(buffer));
    -		file.read(buffer, sizeof(char) * (end_pos - file.tellg()));
    		buffer[left] = '\0';
    		str += std::string(buffer);
    
    	}
    
    		buffer[left] = '\0';
    

    最初の回答では、単に bufferのサイズ変えたくなかったのでコンストラクタで修正しました。

  3. @FubiraiHan

    Questioner

    終端文字について完全に忘れていました。
    回答ありがとうございました。

自分の環境だと、次の行でエラーが出てコンパイルできなかったので、書き換えたところ、問題なく(文字化けすることなく)、test.cppの内容が表示されました。
test.cppUTF-8で書きました)
環境の差でしょうか?

+ #include <cstring>

-	std::ifstream file("test.cpp", std::ios_base::ate, std::ios_base::binary);
+	std::ifstream file("test.cpp", std::ios_base::ate & std::ios_base::binary);
  • Windows11 + MinGW-W64
    (gcc version 15.1.0 (x86_64-posix-seh-rev0, Built by MinGW-Builds project))
  • Windows11 WSL2 (Ubuntu)
    (gcc version 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04))
    どちらも同じ結果
0Like

Comments

  1. @FubiraiHan

    Questioner

    こちらの環境ではファイルを読み込める時と読み込めないときがありましたが、提示していただいた修正点を直すと安定して読み込めるようになりました。
    ありがとうございました。

Your answer might help someone💌