【问题标题】:writing 3 bits at a time to binary file in C一次将3位写入C中的二进制文件
【发布时间】:2021-06-03 16:25:00
【问题描述】:

Image

你好,我有一个位置列表,如存储在链接列表中的图像中所述。每个节点都有一个大小为 2 的无符号字符(代码中的 chessPos)——第一个位置代表一行,第二个位置代表一列。例如第一个节点:row = 'C', col = '5' 等等。该列表通过我不需要构建它的函数传递。

当每行或每列以 3 位写入时,我需要将数据写入二进制文件。所以“C”将被写为 010,“5”之后将被写为 100(写入的 3 位代表行/列 -1,这就是为什么“5”由 100 表示,即二进制的 4)。

困难在于每个字节都是 8 位,每次我将一个字节写入文件时,它都包含 6 位代表一行和一个列,以及下一个字节的 2 位。

我怎样才能让它工作?

谢谢

这是我目前的代码:

      typedef char chessPos[2];
    
    
    typedef struct _chessPosArray {
        unsigned int size;
        chessPos* positions;
    }chessPosArray;
    
    typedef struct _chessPosCell {
        chessPos position;
        struct _chessPosCell* next;
    }chessPosCell;
    
    typedef struct _chessPosList {
        chessPosCell* head;
        chessPosCell* tail;
    }chessPosList;

 

    void function_name(char* file_name, chessPosList* pos_list)
{

    FILE* file;
    short list_len;
    int i = 0;
    unsigned char row, col, byte_to_file, next_byte;
    chessPosCell* curr = pos_list->head;


    file = fopen(file_name, "wb"); /* open binary file to writing */
    checkFileOpening(file);

    
    while (curr != NULL)
    {
        row = curr->position[0] - 'A' - 17; /* 'A' ---> '1' ---> '0' */
        col = curr->position[1] - 1; /* '4' ---> '3' */

        if (remain < 6)
        { 
            curr = curr->next;
            remain += 8;                
        }

        if (i > 1)
        {
            i = 0;
        }

        if (curr->next != NULL)
        {
            next_byte = curr->next->position[i] >> (remain - 7);
            byte_to_file = ((row << (remain - 3)) | (col << (remain - 6))) | (next_byte);
            i++;
        }
        else
        {
            byte_to_file = ((row << (remain - 3)) | (col << (remain - 6)));
        }

        fwrite(&byte_to_file, sizeof(unsigned char), 1, file);

        remain -= 6;                    
             
    }

【问题讨论】:

  • 您目前所拥有的究竟是什么问题?
  • 您需要将所有输出缓存在一个字节数组(unsigned char 的数组或分配块)中,并在完成后将其逐字节写入文件。无法将少于一个字节的内容写入文件。换句话说,在将字节写入文件之前,您需要在每个字节中进行所有位打包。
  • 如果你真的一次需要三个位,你必须将它们编组为一组字节。您至少需要三个字节(24 位)才能与位和字节保持一致。
  • 我怀疑文件大小真的会成为一个很大的问题。我只是将每个位置写成一个完整的字节(浪费 5 位)。但这使得它易于阅读和写作。必要时担心效率,但先让事情变得简单。
  • 除非您尝试遵守现有的数据格式,否则这听起来像是过早优化的情况。您的位置列表中有多少条目,与为每个位置使用 char 相比,通过节省 5 位/位置您真正可以获得多少?

标签: c binaryfiles bit-shift


【解决方案1】:

我怎样才能让它工作?

由于每个位置都需要一列和一行,因此您实际上可以将位置视为单个 6 位值,其中最低 3 位是行,高 3 位是列。如果你这样想,那么问题就简单了一点,因为你实际上只是在谈论 base-64 编码/解码,如果你真的想打包数据,有很多开源实现可用进入尽可能小的空间。

也就是说,我鼓励您考虑您的问题是否真的需要最小化存储空间。您可以改为将这些位置存储为字符,或者使用 4 位表示行,4 位表示列,继续将位置视为 6 位值并忽略额外的两个位。除非您要存储大量此类位置,否则每个位置节省两位的好处可能并不重要。

【讨论】:

    【解决方案2】:

    我怎样才能让它工作?

    好吧,首先从一个好的抽象开始。不管怎样,其实很简单:

    • 让我们取一个 16 位/2 字节的缓冲区和缓冲区中的一个位位置
      • 当缓冲区继续时 (uint16_t) 而不是两个单独的字节 (unsigned char byte_to_file, next_byte) 会更容易。 next_byte 位只是自行移位,byte_to_file 可以使用掩码提取。
    • 我在想象中“看到”了左侧的 MSB 和右侧的 LSB
    • 对于每个新的 6 位,将其推到尚未设置的最左侧位置
      • 所以位移 16-6 减去位置
    • 如果我们填充超过 8 位
      • 取一个字节并输出
      • 并将缓冲区左移 8 位

    这是一个打印Hello world\n的示例程序:

    #include <stdint.h>
    #include <stdio.h>
    
    struct bitwritter {
        FILE *out;
        // our buffer for bits
        uint16_t buf;
        // the count of set bits within buffer counting from MSB
        unsigned char pos;
    };
    
    struct bitwritter bitwritter_init(FILE *out) {
       return (struct bitwritter){ .out = out };
    }
    
    int bitwritter_write_6bits(struct bitwritter *t, unsigned char bits6) {
       // we always write starting from MSB
       unsigned char toshift = 16 - 6 - t->pos;
       // just a mask with 6 bits
       bits6 &= 0x3f;
       t->buf |= bits6 << toshift;
       t->pos += 6;
       // do we have whole byte?
       if (t->pos >= 8) {
          // extract the byte - note it's in MSB
          unsigned char towrite = t->buf >> 8;
          // shift out buffer
          t->buf <<= 8;
          t->pos -= 8;
          // write output
          if (fwrite(&towrite, sizeof(towrite), 1, t->out) != 1) {
                 return -1;
          }
          return 1;
       }
       return 0;
    }
    
    int main() {
        struct bitwritter bw = bitwritter_init(stdout);
        // echo 'Hello world' | xxd -c1 -p | while read l; do python -c "print(\"{0:08b}\".format(0x$l))"; done | paste -sd '' | sed -E 's/.{6}/0b&,\n/g'
        unsigned char data[] = {
            0b010010,
            0b000110,
            0b010101,
            0b101100,
            0b011011,
            0b000110,
            0b111100,
            0b100000,
            0b011101,
            0b110110,
            0b111101,
            0b110010,
            0b011011,
            0b000110,
            0b010000,
            0b001010,
        };
        for (size_t i = 0; i < sizeof(data); ++i) {
            bitwritter_write_6bits(&bw, data[i]);
        }
    }
     
    

    【讨论】:

    • for 循环之后添加对bitwritter_write_6bits(&amp;bw, 0); 的额外调用将​​在必要时清除最后几位数据(即,如果 6 位块的数量不是 4 的倍数) .
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-20
    • 1970-01-01
    • 2017-01-26
    相关资源
    最近更新 更多