web-dev-qa-db-ja.com

Cはバイナリ標準入力を読み取ります

命令パイプラインシミュレーターを構築しようとしていますが、開始するのに多くの問題があります。私がする必要があるのは、stdinからバイナリを読み取り、データを操作している間、それを何らかの方法でメモリに格納することです。正確に32ビットのチャンクを次々に読み取る必要があります。

一度に正確に32ビットのチャンクを読み取るにはどうすればよいですか?次に、後で操作するために保存するにはどうすればよいですか?

これが私がこれまでに得たものですが、私がさらに読んだバイナリチャンクを調べると、それは正しく見えません。私が必要なように正確に32ビットを読んでいるとは思いません。

char buffer[4] = { 0 }; // initialize to 0
unsigned long c = 0;
int bytesize = 4; // read in 32 bits
while (fgets(buffer, bytesize, stdin)) {
  memcpy(&c, buffer, bytesize); // copy the data to a more usable structure for bit manipulation later
  // more stuff
  buffer[0] = 0; buffer[1] = 0; buffer[2] = 0; buffer[3] = 0; // set to zero before next loop
}
fclose(stdin);

一度に32ビットで読み取るには(すべて1/0で、改行はありません)、何に格納するのですか、char[] はい?

編集:私はバイナリを読み取ることができますが、どの答えも正しい順序でビットを生成しません-それらはすべて壊れています、私はエンディアンと問題が一度に8ビット(1文字)を読み取って移動するのに問題があると思います-これWindowsとCで動作する必要があります...?

20
rlb.usa

必要なのは freopen() です。マンページから:

Filenameがnullポインターの場合、freopen()関数は、ストリームに現在関連付けられているファイルの名前が使用されているかのように、ストリームのモードをmodeで指定されたモードに変更しようとします。この場合、freopen()の呼び出しが成功した場合、ストリームに関連付けられたファイル記述子を閉じる必要はありません。どのモードの変更が許可されるか(ある場合)、およびどのような状況で許可されるかは、実装によって定義されます。

基本的に、あなたが本当にできる最善のことはこれです:

_freopen(NULL, "rb", stdin);
_

これにより、stdinが再び開き、同じ入力ストリームになりますが、バイナリモードになります。通常モードでは、Windowsでstdinから読み取ると、_\r\n_(Windows改行)が1文字に変換されますASCII 10. _"rb"_モードの使用この変換を無効にして、バイナリデータを正しく読み取ることができるようにします。

freopen()はファイルハンドルを返しますが、これは以前の値(バイナリモードにする前)なので、何にも使用しないでください。その後、前述のように fread() を使用します。

ただし、懸念事項については、「32ビット」で読み取っていない可能性がありますが、fread()を使用すると、willが読み取られます。 in 4 chars(これは、Cで実行できる最善の方法です-charは少なくともであることが保証されています8ビットですが、一部の歴史的および組み込みプラットフォームには16ビットのcharsがあります(18以下のプラットフォームもあります))。 fgets()を使用すると、決して4バイトで読み取られません。少なくとも3つ(改行であるかどうかによって異なります)を読み込み、4番目のバイトは_'\0'_になります。これは、C文字列がヌル文字で終了し、fgets()nul-読み取り内容を終了するためです(良い関数のように)。明らかに、これはあなたが望むものではないので、fread()を使用する必要があります。

22
Chris Lutz

SET_BINARY_MODEマクロとsetmodeの使用を検討してください。

#ifdef _WIN32
# include <io.h>
# include <fcntl.h>
# define SET_BINARY_MODE(handle) setmode(handle, O_BINARY)
#else
# define SET_BINARY_MODE(handle) ((void)0)
#endif

SET_BINARY_MODEマクロの詳細はこちら: " 標準I/Oを介したバイナリファイルの処理 "

setmodeの詳細はこちら: "_ setmode "

17
Adrian

fgets()はここではすべて間違っています。これは、人間が読める形式のASCIIテキストは、バイナリデータではなく、行末文字で終了することを目的としており、必要なものを取得できません。

私は最近、 read() 呼び出しを使用してあなたが望むことを正確に実行しました。プログラムが明示的にstdinを閉じていない限り、最初の引数(ファイル記述子)には、stdinに定数値0を使用できます。または、POSIXシステム(Linux、Mac OS X、またはその他の最新のUnixバリアント)を使用している場合は、STDIN_FILENOを使用できます。

1
Bob Murphy

上記の親切な人々からのさまざまなコメントから答えをつなぎ合わせる必要があったので、これは完全に機能するサンプルです-Windowsでのみ機能しますが、おそらくWindows固有のものをプラットフォームに翻訳できます。

#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "windows.h"
#include <io.h>
#include <fcntl.h>

int main()
{
    char rbuf[4096];
    char *deffile = "c:\\temp\\outvideo.bin";
    size_t r;
    char *outfilename = deffile;
    FILE *newin;

    freopen(NULL, "rb", stdin);
    _setmode(_fileno(stdin), _O_BINARY);

    FILE *f = fopen(outfilename, "w+b");
    if (f == NULL)
    {
        printf("unable to open %s\n", outfilename);
        exit(1);
    }

    for (;; )
    {
        r = fread(rbuf, 1, sizeof(rbuf), stdin);
        if (r > 0)
        {
            size_t w;
            for (size_t nleft = r; nleft > 0; )
            {
                w = fwrite(rbuf, 1, nleft, f);
                if (w == 0)
                {
                    printf("error: unable to write %d bytes to %s\n", nleft, outfilename);
                    exit(1);
                }
                nleft -= w;
                fflush(f);
            }
        }
        else
        {
            Sleep(10); // wait for more input, but not in a tight loop
        }
    }

    return 0;
}
1
alex gimenez

実行しているOSはわかりませんが、通常は「stdinをバイナリで開く」ことはできません。あなたは次のようなことを試すことができます

int fd = fdreopen (fileno (stdin), outfname, O_RDONLY | OPEN_O_BINARY);

それを強制しようとします。次に、

uint32_t opcode;
read(fd, &opcode, sizeof (opcode));

しかし、私は実際にそれを自分で試したことがありません。 :)

0
Paul Hsieh