【问题标题】:Changing specific bit in large binary file更改大型二进制文件中的特定位
【发布时间】:2015-10-13 06:56:13
【问题描述】:

首先,我正在使用 64 位 Linux 上的大型二进制文件(2-4 GB),该文件也是从另一个进程映射的。因此,文件中的内容总是在不断变化。我想在特定位/位置更改一些位,但不必阅读它。

只改变特定地址的位,不关心它的其余内容 附加编辑:或将位设置为一直卡住。 (那一点不能改变)

当我使用 hexdump 读取时,看起来是这样的。

3ffbd280  00 00 78 32 fb 44 22 cc  44 11 22 aa b2 33 b2 c3

非常欢迎任何想法或想法。

【问题讨论】:

  • 我还不太确定,还在寻找方法。
  • 您需要设置位、重置位还是切换位?如果不是不可能的话,在不事先读取至少包含该位的字节的情况下调整一位是很困难的。位是如何识别的?你在整个文件中有一个位号,还是一个字节号和字节内的位号?你是在 32 位还是 64 位机器上?
  • @JonathanLeffler 可能会切换,我只是希望它得到改变。
  • @JonathanLeffler 我有位/字节数。例如,请查看我的编辑。我使用的是 64 位机器。

标签: c linux file binary


【解决方案1】:

在 64 位 Linux 下,fseek() 的签名不会造成问题; offset 参数是long,这意味着它将是一个 64 位的数量,它可以轻松处理 2-4 GiB 文件。如果问题是针对 32 位 Linux 或 64 位 Windows,情况会有所不同。您在处理 2 GiB 和 4 GiB 之间的偏移时会遇到问题。

假设打开文件流进行读写。假设位位置是通过文件中的绝对字节偏移加上位号 (0…7) 来指定的。假设位应该改变,所以操作将位从 1 切换到 0 或从 0 切换到 1。假设不需要在文件中保留当前位置的记录,以便在翻转位后可以恢复。那么操作顺序可能是:

void flip_bit(FILE *fp, long offset, int bitno)
{
    int c;
    assert(fp != 0);
    assert(offset >= 0);
    assert(bitno >= 0 && bitno < 8);

    fseek(fp, offset, SEEK_SET);
    if ((c = getc(fp)) != EOF)
    {
        c ^= 1 << bitno;
        fseek(fp, -1L, SEEK_CUR);  // Step backwards 1 byte
        putc(c, fp);
        fflush(fp);
    }
}

您更改的每个假设都会触发代码中的相应更改。有必要fflush(fp); 以最大化在磁盘上进行更改的机会,以便其他进程看到它。此外,fflush(fp) 意味着即使没有中间搜索,也可以安全地将流用于输入(或输出)(ISO/IEC 9899:2011 §7.21.5.3 fopen 函数, ¶7):

当以更新模式打开文件时('+' 作为第二个或第三个字符 以上模式参数值列表),输入和输出都可以在 关联的流。但是,输出不得直接跟在输入后面没有 干预对 fflush 函数或文件定位函数的调用 (fseek, fsetpos, or rewind), 输入后面不能直接跟输出 干预对文件定位函数的调用,除非输入操作遇到文件结尾。

如果您希望确保该位已设置,请将分配替换为:

        c |= 1 << bitno;

如果您希望确保该位被重置,请将分配替换为:

        c &= ~(1 << bitno);

【讨论】:

  • 偏移量是指“整个文件中的位/字节地址”吗?
  • 是的。偏移量是文件中的字节位置,从第一个字节的偏移量为零开始,依此类推。字节中的位数是分开的。如果需要,您可以对事物进行不同的编码。但是关于如何识别要翻转的位的问题并不清楚,所以我选择了最简单的解决方案。您可以使用许多变体。如果文件在翻转位的过程中被内存映射,则需要一个完整的替代解决方案。
  • 已成功,非常感谢您的帮助
【解决方案2】:
FILE* fp = fopen( "bigfile.abc", "rb+" )  
//rb+ is important! see here: the wrong mode may clear/delete the file.
//http://www.cplusplus.com/reference/cstdio/fopen/?kw=fopen

if( fp ) 
{
    unsigned char b = 0;
    fseek( fp, #_of_bytes_from_origin, SEEK_SET )
    fread( &b, 1, 1, fp );      //read 1 unit of 1 byte into 'b'

    b ^= (1<<bit_index);        //toggle the bit, as Jonathan said

    fseek( fp, -1, SEEK_CUR );  //go one byte backwards (you went forwards when reading)
    fwrite( &b, 1, 1, fp );     //write 1 unit of 1 byte back into the file
    fclose(fp);                 //close the file when done
}

这只是我多年来在 32 位机器上使用 C 语言的经验,但我认为以上任何内容在 64 位架构上都不会改变,因为您是明确说明一个 字节

见:http://www.cplusplus.com/reference/cstdio/ 以及核心文件函数:fopen()、fclose()、fseek()、ftell()、fread()、fwrite()

更改特定位:

byte_to_change &= ~(1<<bit_index)

其中 bit_index 为 0 到 7。这​​会将位设置为 0。

byte_to_change |= (1<<bit_index)

将其设置为 1。

【讨论】:

  • 我建议这些术语应该是“将位重置为 0”和“将位设置为 1”。您可以将byte_to_change ^= (1 &lt;&lt; bit_index); 添加到“切换位”,这意味着如果它是 1 则将其翻转为 0,如果它是 0 则将其翻转为 1。
  • 上述操作的输入,更重要的是,输出都丢失了。在将字节写回映射文件以便其他进程可以看到它之前,更改内存中的字节对 OP 的帮助不大。
  • 做得很好。唯一的评论是,如果文件大于 2 GiB 并且系统是 32 位系统,您会遇到问题。幸运的是,OP 在 64 位 Linux 系统上,所以没有问题。
  • 非常感谢您的帮助。已经成功了。
猜你喜欢
  • 2013-12-06
  • 1970-01-01
  • 2011-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-13
相关资源
最近更新 更多