質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

8052閲覧

C++ バイナリ形式のファイルを読み込みvectorに格納する方法

ddp

総合スコア17

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/10/08 05:59

編集2020/10/08 08:32

以下のコードは16進数の数値羅列が記述されたテキスト形式のファイルを読み込み、8bitごとの数値を16進数でvector aに格納するコードです。

c++

1#include <fstream> 2#include <iostream> 3#include <sstream> 4#include <string> 5#include <vector> 6 7class read_rom{ 8public: 9 int rom_data; 10 void fetch(std::string filename); 11}; 12 13void read_rom::fetch(std::string filename){ 14 std::ifstream ifs(filename); 15 16 if (!ifs){ 17 std::cout << "Error! File can not be opened" << std::endl; 18 }else{ 19 std::string buf; 20 std::string buf_line; 21 std::vector<int> a; 22 23 while (std::getline(ifs, buf)){ 24 25 std::cout << std::hex << std::showbase << buf << std::endl; 26 std::stringstream ss{buf}; 27 while (std::getline(ss, buf_line, ' ')){ 28 int buf_num = std::stoi(buf_line, nullptr, 16); 29 int up = buf_num >> 8 & 0xff; 30 a.push_back(up); 31 int down = buf_num & 0xff; 32 a.push_back(down); 33 } 34 } 35 std::cout << std::hex << std::showbase << a[16] << std::endl; 36 } 37} 38 39int main(){ 40 read_rom rom; 41 std::string filename = "../test"; 42 rom.fetch(filename); 43} 44 45//読み込んでいるファイル("../test") 467f45 4c46 0101 0100 0000 0000 0000 0000 470300 2800 0100 0000 b125 0000 3400 0000 4854d2 2800 0004 0005 3400 2000 0a00 2800 492b00 2a00 0100 0070 b47e 0200 b47e 0200 50b47e 0200 8008 0000 8008 0000 0400 0000 510400 0000 0600 0000 3400 0000 3400 0000 523400 0000 4001 0000 4001 0000 0500 0000 530400 0000 0300 0000 7401 0000 7401 0000 547401 0000 1900 0000 1900 0000 0400 0000 550100 0000 0100 0000 0000 0000 0000 0000 560000 0000 3887 0200 3887 0200 0500 0000 570000 0100 0100 0000 5090 0200 5090 0300 585090 0300 d00f 0000 e410 0000 0600 0000 590000 0100 0200 0000 209d 0200 209d 0300 60209d 0300 2001 0000 2001 0000 0600 0000 610400 0000 0400 0000 9001 0000 9001 0000

上記のようなテキスト形式ではなく、バイナリ形式のファイルを読み込み、8bitごとの数値を16進数で格納する場合にはどのように記述すれば良いでしょうか。

~~コンパイル時にエラーは無いですが、実行すると以下の画像のよ
うな出力結果となります。
コンパイルコマンドg++ -std=c++14 -g -Og -pipe -Wall
~~
※ascii部分?をうまくコピーできないので、画像で張り付けています。

sublime-text コンソール

画像にもありますが、teminateされています。

terminate called after throwing an instance of 'std::invalid_argument'

what(): stoi

お聞きしたい点は2点あります。

  1. terminateされる理由

stoi部分で不正な引数が使用されているとあるようですが、なぜこのようになるのかわかりません。

  1. 出力結果

~~出力がasciiになっている(一部のみ?)理由がわかりません。
~~

環境はWindowsで以下の通りです。

$ g++ -v

Using built-in specs.
COLLECT_GCC=C:\MinGW\bin\g++.exe
COLLECT_LTO_WRAPPER=c:/mingw/bin/../libexec/gcc/mingw32/9.2.0/lto-wrapper.exe
Target: mingw32
Configured with: ../src/gcc-9.2.0/configure --build=x86_64-pc-linux-gnu --host=mingw32 --target=mingw32 --disable-win32-registry --with-arch=i586 --with-tune=generic --enable-static --enable-shared --enable-threads --enable-languages=c,c++,objc,obj-c++,fortran,ada --with-dwarf2 --disable-sjlj-exceptions --enable-version-specific-runtime-libs --enable-libgomp --disable-libvtv --with-libiconv-prefix=/mingw --with-libintl-prefix=/mingw --enable-libstdcxx-debug --disable-build-format-warnings --prefix=/mingw --with-gmp=/mingw --with-mpfr=/mingw --with-mpc=/mingw --with-isl=/mingw --enable-nls --with-pkgversion='MinGW.org GCC Build-20200227-1'
Thread model: win32
gcc version 9.2.0 (MinGW.org GCC Build-20200227-1)~~

どなたかご教授ください。以上、よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mjk

2020/10/08 07:13 編集

私の環境(Win10+WSL2+VSCode+Ubunts20)ではコードそのままコピペでエラーなく動作しました。 https://gyazo.com/572289c9831ecf2a730b67aaead0c08e 以下、何かしらの参考ヒントになれば幸いです。的外れかもしれませんがあしからず。 エラー関連:terminate called after throwing an instance of https://teratail.com/questions/288345 文字化け関連:文字コードがUTF8かSHIFT-JISかの違いでよく起きます https://teratail.com/questions/152379
FKD

2020/10/08 07:20

質問にはバイナリ形式とありますが、プログラムはテキスト形式で読み込んでいます。 実際のtestファイルはどちらの形式なのでしょうか?
FKD

2020/10/08 07:25

※先頭が0x7fELFなので、バイナリファイル(*.oファイル?)かなと推測しているのですが・・・
ddp

2020/10/08 07:28

上記のような16bitの数値が羅列されているファイルです。 ご指摘の通り、バイナリファイル(01の羅列)ではなく、上記のようなテキスト形式です。 有難うございます。タイトルも変更できるようでしたら、変更します。
ddp

2020/10/08 07:29

>>mjkさん コメントありがとうございます。 なるほど、WSL2 Ubunts20では動作するのですね。。。 当方はWSLではないです。
mjk

2020/10/08 07:42 編集

https://paiza.io/projects/4mIzezc18IQAiithQ14pyQ オンラインコンパイラー(正式名称知らないので仮称です)でもエラーは出ませんでした。 (ファイル名の指定だけ少し変更してます。) なので実行環境に原因がありそうかなと推測。
ddp

2020/10/08 07:55

>mjkさん オンラインコンパイラーで検証頂きありがとうございます。 なるほど。。実行環境はWindows、migwになります。 g++ -v の出力を質問文に追記しました。
yohhoy

2020/10/08 08:07 編集

> 上記のような16bitの数値が羅列されているファイルです。 > ご指摘の通り、バイナリファイル(01の羅列)ではなく、上記のようなテキスト形式です。 と仰っていますが、実行結果は「バイナリファイル」を読み込んだと解釈すれば2点とも説明可能です。 テキストファイルとバイナリファイルについて誤解されているようです。
mjk

2020/10/08 08:22 編集

私も素人なので直接の原因を突き止められることは思いつかないのですが、こちらは32bit環境では無く64bit環境です。 あと気になることはVSCodeをお使いなのでしょうか別のIDE? 出力先はコマンドプロンプト? testファイルの保存形式はUTF8になっている? (Win10のアプデ次第ではメモ帳の保全形式はANSI=SHIFT-JISがデフォルトだったはず)
ddp

2020/10/08 08:21

>yohhoyさん ご指摘有難うございます。 再度確かめてみると、バイナリ形式でした。。皆さま誠に申し訳ありません。 sublimetextで見ると、勝手に16bit表記に変換されて表示されるようで、勘違いしていました。 vimで見るとばっちりバイナリ形式でした。 バイナリ形式のデータを、8bitずつvectorに16進数で格納するにはどのように記述すれば良いでしょうか。 タイトルなど、のちほど改めさせて頂きます。
ddp

2020/10/12 04:22

>>mjkさん 情報有難うございます。 vectorについて、回答者の情報と相まって大変勉強になりました。
guest

回答2

0

ベストアンサー

バイナリ形式のファイルを読み込み、8bitごとの数値を16進数で格納する場合にはどのように記述すれば良いでしょうか。

int も char も内部は 2進数です。
16進数というのはソースコード上の表現、または文字列としての表現です。
キーボード入力や画面出力は文字列で行うのでその表現でもあります。

8ビットの数値を格納するのに、int は無駄なので vector<char> で十分です。
最初にファイルのサイズを取得しておけば、
vector<char> a(size); で領域を確保でき、read 1回で読み込めます。

C++17 であれば、std::filesystem::file_size でファイルサイズが取得できます。

C++

1#include <iostream> 2#include <fstream> // ifstream 3#include <iomanip> 4#include <string> 5#include <vector> 6#include <filesystem> 7 8int main() 9{ 10 std::string name = "test"; 11 size_t size = std::filesystem::file_size(name); 12 13 std::ifstream ifs(name, std::ios::binary); 14 if (!ifs) return 1; 15 16 std::vector<char> a(size); 17 if (!ifs.read(&a[0], size)) return 2; 18 19 std::cout << std::hex << std::setfill('0'); 20 size_t i = 0; 21 while (i < size) { 22 std::cout << ' ' << std::setw(2) << (a[i] & 0xff); 23 if ((++i & 15) == 0) std::cout << std::endl; 24 } 25 if (i & 15) std::cout << std::endl; 26}

g++ -std=c++17 でコンパイルします。

追記
unsigned(a[i]) を (a[i] & 0xff) に修正しました。

投稿2020/10/10 04:44

編集2020/10/12 04:24
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ddp

2020/10/12 04:19

ご回答有難うございます。 ご連絡遅くなってしまい申し訳ありません。 c++17にてコンパイルして実行してみたところ、ほぼ所望の動作をしてくれました! 有難うございます。 1点、出力によくわからない点があります。 出力に一部ffffffが接頭するケースがあり、これが昨日から考えているのですが、 原因がよくわかりません。 <欲しい出力> 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 03 00 28 00 01 00 00 00 b1 25 00 00 34 00 00 00 54 d2 28 00 00 04 00 05 34 00 20 00 0a 00 28 00 2b 00 2a 00 01 00 00 70 b4 7e 02 00 b4 7e 02 00 b4 7e 02 00 80 08 00 00 80 08 00 00 04 00 00 00 <実際> 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 03 00 28 00 01 00 00 00 ffffffb1 25 00 00 34 00 00 00 54 ffffffd2 28 00 00 04 00 05 34 00 20 00 0a 00 28 00 2b 00 2a 00 01 00 00 70 ffffffb4 7e 02 00 ffffffb4 7e 02 00 ffffffb4 7e 02 00 ffffff80 08 00 00 ffffff80 08 00 00 04 00 00 00 何かお分かりになりましたら、ご連絡頂けると助かります。
kazuma-s

2020/10/12 04:22

unsigned(a[i]) は間違いでした。(unsigned char)a[i] にしてください。 static_cast<unsigned char>(a[i]) でもいいし、(a[i] & 0xff) でもいいでしょう。
ddp

2020/10/12 04:40

早速ご連絡頂き、有難うございます。 まずは所望の動作を得られたので、ベストアンサーさせて頂きます。 & 0xff についてなのですが、 たしかにこれで解決することはわかります。 ただ気になってしまうのが、なぜffffffが接頭するケースがあるのか。。。 こちら何か理由があるのでしょうか。 お分かりな点がありましたら、ご連絡頂けると助かります。
kazuma-s

2020/10/12 05:15 編集

すみません。(unsigned char)a[i] も static_cast<unsigned char>(a[i]) もダメでした。 a[i] が 0x45 だったとします。 << a[i] で出力すると、a[i] が char なので 'D' という文字が表示されます。 a[i] を unsigned char に変換しても同じです。 そこで、int か unsigned int への変換が必要になります。 char は 0x00~0xff の 1バイトの値を持ちますが、char が符号付きか符号無しかは処理系依存です。 たいていのコンパイラは char を符号付きとして扱っています。 0x00~0x7f は 0~127 という値、0x80~0xff は -128 ~ -1 という値になります。 たとえば、a[i] が 0xb1 の時、その値は 176 ではなく、-79 です。 そして int の -79 は 0xffffffb1 です。 このように 0x80~0xff は最上位ビットが 1 なので負の値と解釈され、 符号拡張により上位24ビットが全部 1 になるのです。 (unsigned int) に変換しようとしても、 式の中で演算を行うときは char は暗黙のうちに int に変換されるので、 変換された負の値が unsigned int になるだけで、ビットパターンは変わりません。 << (int)(unsigned char)a[i] ならいいでしょう。
ddp

2020/10/13 03:46

回答有難うございます。 すばらしくわかりやすい説明でした。 大変参考になりました。 この度は有難うございました。
guest

0

バイナリ形式のファイルを読み込み、8bitごとの数値を16進数で格納する場合

実行効率を気にしなくてよければ、下記コードが一番シンプルかと思います。

c++

1std::ifstream ifs(filename, std::ios::binary); 2std::vector<char> result( 3 std::istreambuf_iterator<char>{ifs}, 4 std::istreambuf_iterator<char>{} 5); 6// resultにファイル内容がすべて読み込まれる

投稿2020/10/08 08:35

編集2020/10/08 08:37
yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ddp

2020/10/08 09:43

ご回答有難うございます。 std::cout << result[n] << std::endl; でアクセスするとnがいくつでも1が返ってきます。 アクセスの仕方が間違っていますでしょうか。
yohhoy

2020/10/08 15:03

cout << result.size(); などで実際に読込めたファイルサイズを確認されてはいかがでしょう。 ちなみにresult[n]はchar型ですから、cout << result[n]は文字として整形出力します。数値(バイト値)として出力確認したい場合は、int型へキャストする必要があります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問