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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

integer

integerは、一般的に整数を表します。プラスやマイナス、ゼロもなりうる全ての数です。(例 : -2, -1, 0, 1, 2...)

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Q&A

解決済

3回答

7599閲覧

C言語がfwriteしたintのサイズがおかしい?

sititou70

総合スコア7

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

integer

integerは、一般的に整数を表します。プラスやマイナス、ゼロもなりうる全ての数です。(例 : -2, -1, 0, 1, 2...)

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

0グッド

0クリップ

投稿2016/12/30 12:45

###実現したいこと
C言語がfwriteしたintをNode.jsで読みたい。

###発生している問題
次のコードを見てください。
C言語がintの「789129」をfwriteしています。

C

1int main(){ 2 FILE* fp = fopen("hoge.dat", "w"); 3 int num = 789129; 4 fwrite(&num, sizeof(int), 1, fp); 5 fclose(fp); 6}

このプログラムを実行して得られたhoge.datをバイナリエディタで見ると、次のようになっていました。

00010203 04050607 08090A0B 0C0D0E0F 0000 890D0A0C 00

私の処理系では、sizeof(int)は4(32bit)を返します。
それなのに、ファイルには5バイトのデータが書き込まれています。
この数値を、例えばNode.jsのreadInt32LEを使って読もうとすると、当然おかしな数値が返ってきてしまいます。
諸事情により、C言語側のプログラムを修正することは出来ません。
何故5バイトのデータが書かれてしまうのか、また
どのようにすればNode.jsでこのデータを解釈できるのか。

教えてほしいです。

####補足
この問題は常に起こるわけでは無いようです。
例えば「7891290」を先程のCのプログラムでfwriteしてみると、

00010203 04050607 08090A0B 0C0D0E0F 0000 5A697800

のようになっており、4バイトのデータが書き込まれています。
これはリトルエンディアンの32bitIntで「7891290」ですから、readInt32LEで読めるのですが…。

####Cのコンパイル環境
Win10(x64)で、gcc4.8.1を使っています。

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

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

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

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

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

guest

回答3

0

fopen("hoge.dat", "w")(テキストモード)ではなくfopen("hoge.dat", "wb");(バイナリモード)とすべきです。

Windows環境のテキストモードでは、LF(\n)/0x0A は CR+LF(\r+\n)/0x0D+0x0A という2バイトへと変換されてしまいます。バイナリデータを出力するときは、バイナリモードの指定は必須です。C言語プログラムとしての致命的なバグですから、原則としてC言語プログラム側を修正すべきです。


しかし、今回のケースではCのプログラムには変更を加えられません。
このソースで書かれたデータをNode.jsが解釈する方法はありますか?

残念ながら、Node.js側で正しく復号(解釈)するのはおそらく不可能です。

復号処理は「バイト並び0x0D+0x0Aを0x0Aへと戻す」のですが、正しいオリジナルデータの時点で0x0D+0x0Aの場合も誤って0x0Aへと逆変換してしまいます。言い換えると、ファイルに0x0D+0x0Aバイト並びがあったとき、それがオリジナルの0x0D+0x0Aという2バイトだったのか、0x0Aという1バイトだったのかを判断する手段がありません。何らかの仮定やデータ範囲の制約条件を置けるならば、復号できる可能性があるかもしれませんが...

投稿2016/12/30 13:00

編集2016/12/30 13:37
yohhoy

総合スコア6191

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

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

sititou70

2016/12/30 13:02

ありがとうございます。 ちゃんとreadInt32LEで読めました。 しかし、今回のケースではCのプログラムには変更を加えられません。 このソースで書かれたデータをNode.jsが解釈する方法はありますか?
guest

0

ベストアンサー

こんにちは。

これはWindows特有の問題です。テキスト・モードてオープンしたファイルへ改行コードを出力するとCRLFへ変換されてしまいます。今回はLF(0x0A)を出力したらCRLF(0x0D, 0x0A)へ変換されたケースですね。
他にも何か自動変換があるかもしれませんが、良く分かりません。

他になければ、バイナリの状態で0x0D, 0x0Aを検出したら0x0Aへ変換してから、数値へデコードすればできる筈です。でも、かなり無理矢理なので何か思わぬ不具合ができるかもです。C側を変えるのが「まっとう」なやりかたです。

後、もしも、リビルドできるのでしたら、bash on windows上で走らせるとか。
linuxはテキスト・モードとバイナリ・モードの差はなかった筈ですので、このような変換はされないと思います。
でも、bash on windowsはまだβですし、いろいろ問題を抱えているようですので、あまりお勧めできません。

思い出しました。API Hookするなんて無茶もあります。
MinGW(gcc)のfopenは恐らくCreatFileA()を呼び出していると思います。
CreateFileA()をすげ替えて、ファイル名で問題の呼び出しかどうか判定し、問題の呼び出しならテキスト・モードからバイナリ・モードへすげ替える、そうでないときはCreateFileA()へパススルーすることでやってやれないことはないかも知れません。

投稿2016/12/30 13:34

編集2016/12/30 13:45
Chironian

総合スコア23272

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

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

sititou70

2016/12/30 15:57

ありがとうございます。「バイナリの状態で0x0D, 0x0Aを検出したら0x0Aへ変換」することにします。 「bash on windows上で走らせる」 「API Hook」 すみません。Cのプログラムはexeにコンパイルされていて、修正が難しいということを書いておくべきでした…。 上記の方法で無事に解決できました。 ありがとうございました。
guest

0

自分の環境はWindows10 cygwin(64bit) gcc (GCC) 5.4.0ですが、こういったことは発生しませんでした。つまりLinuxと同様に改行コードLFがCR,LFに置き換わって出力されるということはやってないようです。想像するにcyginはMinGWよりUnixライクな環境をエミュレートしているということなのかと思います。もしそうならこれはMinGW固有の振る舞いということなのかも知れないと思いました。

どのようにすればNode.jsでこのデータを解釈できるのか

本来はyohhoyさんがおっしゃるようにソースを直せればよいのですが、無理ということなので回避策を考えてみると・・・

おそらくはfwriteの振る舞いはNL(0x0A)->CR,NL(0x0D,0x0A)へ変換するだけだと思います(念のため0x00~0xFFまで実際にfwriteの結果がどうなるか確認すべきかと思います)が、もしそうなら結果のファイルの中の0x0Aというバイトは0x0Dの直後にしか存在しないはずです。よってreadInt8を使って1バイトずつ読みながら0x0D,0x0Aのシーケンスを見つけたらそれを0x0Aと解釈する方法で元のバイト列が回復できると思います。そのようにして4バイト分を読み込んだらそれを(リトルエンディアンを意識しつつ)intへ変換してやれば元のデータが再現できます。この処理を関数として自前で定義してそれをreadInt32LEの代わりに用いることで回避策になると思います。

やむをえないとはいえこういった回避策はおっかないですね。メンテナンスにえらく神経を使わなければいけなさそうなので・・・

投稿2016/12/30 16:17

KSwordOfHaste

総合スコア18394

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

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

sititou70

2016/12/30 17:19

ありがとうございます。 cyginでは挙動が違うのですね。勉強になります。 ご指摘の回避策でなんとか動いている状況です。怖いですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問