最初の答えは、本来の8倍のスペースを必要とするため、質問に正しく答えません。
どうしますか?たくさんの真/偽の値を保存するために本当に必要です。
最も簡単な方法: 連続する 8 つのブール値を取得し、それらを 1 バイトとして表し、そのバイトをファイルに書き込みます。それは多くのスペースを節約します。
ファイルの先頭に、ファイルに書き込みたいブール値の数を書き込むことができます。その数は、ファイルからバイトを読み取り、それらをブール値に変換するときに役立ちます!
バイナリへの変換を最もよくサポートするビットセット クラスが必要であり、ビットセットが unsigned long のサイズより大きい場合、使用する最適なオプションはboost::dynamic_bitsetです。(スペースの節約を心配している場合は、32ビット以上、さらには64ビット以上であると思います)。
dynamic_bitset から to_block_range を使用して、基になる整数型にビットを書き込むことができます。from_block_range または BlockInputIterator からのそのコンストラクターを使用するか、append() 呼び出しを行うことにより、ブロックから dynamic_bitset を構築できます。
これで、バイトがネイティブ形式 (ブロック) になりましたが、ストリームに書き込んで読み戻すという問題がまだ残っています。
最初に少しの「ヘッダー」情報を保存する必要があります。ブロックの数と、場合によってはエンディアンです。または、マクロを使用して標準のエンディアンに変換することもできます (たとえば ntohl ですが、理想的には、最も一般的なプラットフォームでは操作を行わないマクロを使用します。そのため、それがリトルエンディアンの場合は、おそらくその方法で保存し、ビッグエンディアン システム)。
(注: boost::dynamic_bitset は、基になるエンディアンに関係なく、整数型を同じ方法で標準的に変換すると想定しています。ドキュメントには記載されていません)。
2 進数の数値をストリームに書き込みos.write( &data[0], sizeof(Block) * nBlocks )
、読み取るには is を使用します。read( &data[0], sizeof(Block) * nBlocks )
ここで、 data は であると想定されvector<Block>
、読み取る前に実行する必要がありますdata.resize(nBlocks)
( ではありませんreserve()
)。istream_iterator
( orで奇妙なことを行うこともできますistreambuf_iterator
が、 resize() の方がおそらく優れています)。
ビットセットを圧縮せずに、最小数のバイトを使用する 2 つの関数を試してみます。
template<int I>
void bitset_dump(const std::bitset<I> &in, std::ostream &out)
{
// export a bitset consisting of I bits to an output stream.
// Eight bits are stored to a single stream byte.
unsigned int i = 0; // the current bit index
unsigned char c = 0; // the current byte
short bits = 0; // to process next byte
while(i < in.size())
{
c = c << 1; //
if(in.at(i)) ++c; // adding 1 if bit is true
++bits;
if(bits == 8)
{
out.put((char)c);
c = 0;
bits = 0;
}
++i;
}
// dump remaining
if(bits != 0) {
// pad the byte so that first bits are in the most significant positions.
while(bits != 8)
{
c = c << 1;
++bits;
}
out.put((char)c);
}
return;
}
template<int I>
void bitset_restore(std::istream &in, std::bitset<I> &out)
{
// read bytes from the input stream to a bitset of size I.
/* for debug */ //for(int n = 0; n < I; ++n) out.at(n) = false;
unsigned int i = 0; // current bit index
unsigned char mask = 0x80; // current byte mask
unsigned char c = 0; // current byte in stream
while(in.good() && (i < I))
{
if((i%8) == 0) // retrieve next character
{ c = in.get();
mask = 0x80;
}
else mask = mask >> 1; // shift mask
out.at(i) = (c & mask);
++i;
}
}
おそらく、ビットセットが使用するメモリの一部の reinterpret_cast を文字の配列として使用することもできますが、ビットセットの表現が何であるかがわからないため、システム間で移植できない可能性があることに注意してください(エンディアン?)
1つの方法は次のとおりです。
std::vector<bool> data = /* obtain bits somehow */
// Reserve an appropriate number of byte-sized buckets.
std::vector<char> bytes((int)std::ceil((float)data.size() / CHAR_BITS));
for(int byteIndex = 0; byteIndex < bytes.size(); ++byteIndex) {
for(int bitIndex = 0; bitIndex < CHAR_BITS; ++bitIndex) {
int bit = data[byteIndex * CHAR_BITS + bitIndex];
bytes[byteIndex] |= bit << bitIndex;
}
}
これは、何の調整も行わないため、ビット レイアウトが最終的にメモリ内にあることを気にしないことを前提としていることに注意してください。ただし、実際に格納されたビット数もシリアル化する限り (ビット数が CHAR_BITS の倍数ではない場合をカバーするために)、元々このように持っていたものとまったく同じビットセットまたはベクターを逆シリアル化できます。 .
(私はそのバケット サイズの計算に満足していませんが、午前 1 時であり、より洗練されたものを考えるのに苦労しています)。
#include "stdio"
#include "bitset"
...
FILE* pFile;
pFile = fopen("output.dat", "wb");
...
const unsigned int size = 1024;
bitset<size> bitbuffer;
...
fwrite (&bitbuffer, 1, size/8, pFile);
fclose(pFile);
2 つのオプション:
より大きなディスクには、余分なポンド (またはペンス、より可能性が高い) を費やしてください。
ビットセットから一度に 8 ビットを抽出し、それらをバイトに構成して、出力ストリームに書き込むルーチンを作成します。