std::ostringstream に取り込んだバイナリデータのhex表示

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
chibago

std::ostringstream に取り込んだバイナリデータのhex表示

#1

投稿記事 by chibago » 14年前

皆様、お久しぶりです。chibagoです。
今回、御教授いただきたいのはstd::ostringstreamに取り込んだバイナリデータのhex表示に
関してのものです。

現在、boostのシリアライズ機能を利用して、バイナリにシリアライズしたデータを
sqliteのバイナリ形式(blob)としてデータベースに取り込みたいと思っております。

大体の所まではいっているのですが、もうちょっとのところでつまづいております。

流れとしては

コード:

  std::ostringstream os;
  boost::archive::binary_oarchive bo(os);
  bo << (const mydata<float>&) info;
 
    std::string query = "insert into my_table(mydata) values( ? );";

    sqlite3_stmt *statement;

    if(sqlite3_prepare_v2(database, query.c_str(),
                          -1, &statement, 0) == SQLITE_OK){
      int rc = 0;
      rc = sqlite3_bind_blob(statement, 1, os.str().c_str(), os.str().length(),
			     NULL); 

これは、シリアライズ単体の部分と、bind_blobを通してstd:stringを受け渡し(hello world等の文字列で)は
それぞれ動作を確認しておりますが、バイナリデータをc_str()で吐き出すときにC_strで表現できない
部分が無くなっているようです。(標準出力にstringのままとc_strで出した場合とでの表示が異なります。)

これを回避する方法としてバイナリデータをHeX表示で受け渡しすることを考えております。(もともと、sqliteの
バイナリはhexでやりとりするのが普通のようです。)

c_strにHeXで吐き出す方法はございますでしょうか。
ご教授いただければ幸いです。

また、他の解決法がございましたらそちらも歓迎致します。

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#2

投稿記事 by h2so5 » 14年前

c_str()はCの文字列として出力するので、途中にNULLがあるとそこまでしか出力されません。
バイナリデータをそのまま取り出すには data() を使います。

かずま

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#3

投稿記事 by かずま » 14年前

sqlite3_prepare_v2 の第3引数のバイト数が負だと、
query.c_str() の途中の '\0' の前までの長さと解釈されます。
-1 を query.size() または query.length() に変えてみてください。

かずま

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#4

投稿記事 by かずま » 14年前

かずま さんが書きました:sqlite3_prepare_v2 の第3引数のバイト数が負だと、
query.c_str() の途中の '\0' の前までの長さと解釈されます。
とんでもない勘違いでした。query は上で定義されていて '\0' なんか途中にありませんね。
query ではなく、os.str() の問題ですね。

かずま

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#5

投稿記事 by かずま » 14年前

愚直ですが、これでどうでしょうか?

コード:

#include <iomanip>

	std::ostringstream os2;
	os2 << std::hex << std::setfill('0');
	for (size_t i = 0;  i < os.str().length(); i++)
		os2 << std::setw(2) << (os.str().c_str()[i] & 0xff);
	rc = sqlite3_bind_blob(statement, 1, os2.str().c_str(),
			os2.str().length(), NULL);

chibago

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#6

投稿記事 by chibago » 14年前

かずま様、h2so5様、
お返事ありがとうございます。

data()の件は試しましたがダメでした。
std::coutで見る限り(これがダメかもしれませんが)はc_str()と同様の
動作に思えます。

かずま様にはコーディングまで示していただきありがとうございます。
自分で16進数と言い出してあつかいをあまりしりませんでした。

今度は受け手側でこまっております。
select文でバイナリデータを取り出しistringstrea経由で
デシリアライズを試みております。

コード:

      std::istringstream is((char*)sqlite3_column_blob(statement, 0));
      boost::archive::binary_iarchive bi(is);
      bi >> my_data;
どうも、文字列で受け取る際に先ほどの格納の際と同様の障害が起こっているようです。

理解が浅く応用が効きません、
どのようなコーディングになるのでしょうか。

教えていただけると助かります。

chibago

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#7

投稿記事 by chibago » 14年前

sqliteにhexと言う関数があり

コード:

select hex(mydata)
で16進数の文字列で取り出す所まではいきました。

ただ、今度は16進数を通常のバイナリデータに変換する必要があります。

お分かりになる方は教えていただければ幸いです。

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#8

投稿記事 by h2so5 » 14年前

chibago さんが書きました: data()の件は試しましたがダメでした。
std::coutで見る限り(これがダメかもしれませんが)はc_str()と同様の
動作に思えます。
それはstd::coutに出力すると文字列として表示されるからです。
バイナリデータをstd::coutでそのまま出力しても正常に表示されません。

あと、HEXとしてやりとりするのはコマンドラインからSQliteを扱う場合であって、
Cの関数からはそのままバイナリデータで格納できます。
わざわざHEXに変換するのは効率が悪いと思います。
chibago さんが書きました:sqliteにhexと言う関数があり

コード:

select hex(mydata)
で16進数の文字列で取り出す所まではいきました。

ただ、今度は16進数を通常のバイナリデータに変換する必要があります。

お分かりになる方は教えていただければ幸いです。
16進数の文字列に変換したものをさらに16進数に変換したら意味がないですよ。
文字列として格納しているのだからそのまま16進数の文字列として取り出せるはずです。

chibago

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#9

投稿記事 by chibago » 14年前

h2so5 様、
おっしゃるとおりです。

16進数にしたものをさらにHEX関数をかぶせたのは勘違いでした。
コンパイル時の古いオブジェクトファイルが残っていたため混乱してしまいました。

今の課題は、16進数を通常のバイナリへ戻すことです。

確かに、これは効率の悪いものです。

直接バイナリデータデやりとり出来るのが理想です。
ただ、DATA()がダメだったのはCOUTだけでは無く、
プログラムへの実装の方もです。

COUTで見てみたのはより多くの情報を得たかったためです。

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#10

投稿記事 by h2so5 » 14年前

chibago さんが書きました: 直接バイナリデータデやりとり出来るのが理想です。
ただ、DATA()がダメだったのはCOUTだけでは無く、
プログラムへの実装の方もです。
sqlite3_bind_blobの第五引数にSQLITE_TRANSIENTを指定するとどうでしょうか?

chibago

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#11

投稿記事 by chibago » 14年前

h2so5 様、

TRANSIENTでいけました。

ただ、select文で取得されるものは16進数のもの(確かに、わざわざ変換したのはばかばかしいですね。)ですので、何とかBINARYに戻す必要があります。

簡単な方法はございますでしょうか。

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#12

投稿記事 by h2so5 » 14年前

hex関数を使わずに sqlite3_column_blob を使って直接バイナリで取得できると思います。

chibago

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#13

投稿記事 by chibago » 14年前

HEXは使っていないんです。

デシリアライズはうまくいかず
std::cout<<(char*)sqlite3_column_blob(statement, 0)<<std::endl;
で吐き出される文字列は16進数のようですので、
sqlite3_column_blobで取得できるのは16進数形式だけ(?)
なのでしょうか。

それとも、凡ミスでしょうか。

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#14

投稿記事 by h2so5 » 14年前

現状のコードを見せてください。
少なくとも、sqlite3_column_blobを使うとHEX文字列に変換されるという仕様はないようです。

chibago

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#15

投稿記事 by chibago » 14年前

間違いなく動いているINSERT側です

コード:

  std::ostringstream os;
  boost::archive::binary_oarchive bo(os);
  bo << (const MyData<float>&) info;
  
    std::string query = "insert into my_table(comment, my_data) values('" + comment + "', ? );";
    sqlite3_stmt *statement;

    if(sqlite3_prepare_v2(database, query.c_str(),
                          -1, &statement, 0) == SQLITE_OK){
      int rc = 0;
      rc = sqlite3_bind_blob(statement, 1, os.str().data(), os.str().length(),
			     SQLITE_TRANSIENT); 
      rc = sqlite3_step(statement);
        sqlite3_finalize(statement);
    }

SELECT側です。

コード:

   sqlite3_stmt *statement;
    Mydata<float> mi;

    std::string query = "select my_data from my_table where comment = '" + comment + "';";
    if(sqlite3_prepare_v2(database, query.c_str(),
                          -1, &statement, 0) == SQLITE_OK){
      int rc = sqlite3_step(statement);
      std::cout<<(char*)sqlite3_column_blob(statement, 0)<<std::endl;
      std::istringstream is((char*)sqlite3_column_blob(statement, 0));
     
      boost::archive::binary_iarchive bi(is);
      bi >> mi;
      sqlite3_finalize(statement);
    }
間違い探しにつき合わせてしまってすみません。
キャスト等は理解せずにサンプルの受け売りですので、
自分でも試行錯誤でためしてみたいとおもいます。

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#16

投稿記事 by h2so5 » 14年前

std::stringのコンストラクタにバイナリデータを渡すときは、データ長を与える必要があります。

コード:

std::string buffer( (const char*)sqlite3_column_blob(statement, 0), sqlite3_column_bytes(statement, 0) );
std::istringstream is(buffer, istringstream::binary);

chibago

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#17

投稿記事 by chibago » 14年前

h2so5 様、
ありがとうございます。

ただ、教えていただいたとおりプログラムを変更しても
デシリアライズには失敗してしまいます。

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#18

投稿記事 by h2so5 » 14年前

入れたときのバイナリデータと取得したときのバイナリデータをHEXで出力して比較してはどうでしょう。

chibago

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#19

投稿記事 by chibago » 14年前

入力側はかずま様が教えてくださった変換で、出口はh2so5 様のbufferをCOUTで出したもので
1600000073657269616c697a6174696f6e3a3a6172636869766509000404040801000000000000000004000000746573740100004842
のような形で完全に一致して居ります。

ちなみに、
std::cout<<(char*)sqlite3_column_blob(statement, 0)<<std::endl;
でも同様な出力が得られますので、出口までは正しいデータが来ていると思われます。

ちなみに、sqliteを通さずにシリアライズ、デシリアライズの実績はございまして、
coutで出力する形の
serialization::archive testHB
(コンソール上では見たことのない絵文字のようなものが現れていますが)
のようにならなければならないと思われます。

推測ですが
std::istringstream is(buffer, std::ios::binary);
のbufferはbinary形式に変換されたもの(ORビット列を表したもの)
でなければならないようなきがします。

chibago

Re: std::ostringstream に取り込んだバイナリデータのhex表示

#20

投稿記事 by chibago » 14年前

h2so5様、
ありがとうございました。

コードがごちゃごちゃしてきましたので
整理してみたら動きました。

基本的にh2so5様からいただいた情報でいけました。

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

閉鎖

“C言語何でも質問掲示板” へ戻る