【问题标题】:Problem with hex literal in string comparison字符串比较中的十六进制文字问题
【发布时间】:2009-09-07 18:14:24
【问题描述】:

我正在读取一个 NES ROM 文件,其中前四个字节是“\x4e\x45\x53\x1a”或 NES\x1a。在我的实际代码中,给定的文件可以是任意的,所以我想检查以确保这个头文件在这里。但是,我遇到了一些麻烦,下面的代码演示了:

#include <stdio.h>
#include <string.h>

int main()
{
    FILE *fp;
    fp = fopen("mario.nes", "rb");

    char nes[4];
    char real_nes[4] = "NES\x1a";
    fread(nes, 4, 1, fp);
    printf("A: %x\n", nes[3]);
    printf("B: %x\n", real_nes[3]);
    printf("C: %s\n", nes);
    printf("D: %s\n", real_nes);
    if (strcmp(nes, real_nes) != 0) {
        printf("not a match\n");
    }
    fclose(fp);
    return 0;
}

返回:

A: 1a
B: 1a
C: NES?
D: NES
not a match

问号是 \x1a。

我是 C 的新手,所以我可能遗漏了一些微妙的(或明显的)关于为什么两个字符串不匹配,以及为什么打印行 D 时不显示问号,以表示\x1a 在字符串的末尾,B 行似乎表明它应该是。

【问题讨论】:

    标签: c hex fopen strcmp


    【解决方案1】:

    一些意见和建议:

    • 以二进制模式打开文件 - 否则,在非 POSIX 系统上可能会发生有趣的事情(已修复

      fp = fopen("mario.nes", "rb");
      
    • 如果您想打印或比较缓冲区或使用像strncmp() 这样接受字符串长度作为额外参数的函数,请以空值终止您的缓冲区

      printf("C: %.4s\n", nes);
      printf("D: %.4s\n", real_nes);
      if (strncmp(nes, real_nes, 4) != 0) {
      
    • '\x1a' 是非图形替换字符^Z

    • 检查 io 函数的返回值是否有错误

    【讨论】:

    • strncmp() 比较正确,因此我将其归结为由于没有空终止而导致的一些怪异。谢谢。
    【解决方案2】:

    嗯,一个问题是您对 strcmp 的使用。此函数需要一个以零结尾的字符串(在您的代码中,nesreal_nes 都不是以零结尾的字符串)。

    另一个问题是fread。像这样使用它:

    fread(nes, 1, 4, fp); // first size_t param is size and second is member count
    

    像这样更改您的代码:

    int main()
    {
            FILE *fp;
            fp = fopen("mario.nes", "rb");
    
            char nes[5];
            char real_nes[5] = "NES\x1a";
            fread(nes, 1, 4, fp);
            nes[4] = '\0';
            printf("A: %x\n", nes[3]);
            printf("B: %x\n", real_nes[3]);
            printf("C: %s\n", nes);
            printf("D: %s\n", real_nes);
            if (strcmp(nes, real_nes) != 0) {
                printf("not a match\n");
            }
            fclose(fp);
            return 0;
    }
    

    然后看看它是否有效。

    【讨论】:

    • Pablo,你为什么要把fread(nes, 4, 1, fp) 改成fread(nes, 1, 4, fp),毕竟它们不是都读4 个字节吗? (这是一个问题,而不是挑战)
    • 是的,它们都在读取 4 个字节。但是您应该将每个成员的大小放在第一个参数上,并在第二个参数上读取计数。由于他正在读取字符(1 个字节),因此第一个参数应该是 1。这样,如果您捕获 fread 的返回值(读取的元素数),您将获得正确的大小。
    【解决方案3】:

    您的代码中的主要问题是:

    char real_nes[4] = "NES\x1a";
    

    这不是字符串,因为它不以 nul 终止符字符 ('\0') 结尾。 'nes' 也是同样的问题。

    只需像这样声明它们:

    char real_nes[] = "NES\x1a"; /* this is a string, ended by '\0' */
    char nes[sizeof real_nes];
    

    要确保 '\0' 有足够的位置。

    现在您可以使用 %s 说明符或 strcmp()。无论如何,我建议使用 strncmp() 代替,例如:

    if(0 != strncmp(real_nes, nes, sizeof real_nes)) { /* some stuff */ }
    

    HTH。

    【讨论】:

    • 感谢关于字符串的更全面的解释。
    • 即使在这个例子中,在执行fread(nes, 4, 1, fp); 时,您并没有将空终止符添加到 nes[4],因此比较也可能不起作用。应该添加nes[4] = 0;
    【解决方案4】:

    不要在非零终止字节数组上使用字符串函数。

    问题是您有两个 4 字节数组,其中应包含字符串“NES\x1a”(没有为 '\0' 留下空间,因为它已经是 4 个字节长),但是 %s 格式和 strcmp 需要一个'\0' 终止在末尾知道字符串结束。这就是它无法正常工作的原因。

    1.: 不要在这个字节数组上使用带有 %s 格式的 printf。 2.: 使用 memcmp 比较字节数。

    试试这个:

    int i;
    
    printf("Read bytes: 0x");
    for(i = 0; i < sizeof(nes); i ++)
      printf("%02X", nes[i]);
    printf("\n");
    
    if (memcmp(nes, real_nes, sizeof(nes)) != 0) {
      printf("not a match\n");
    }
    

    【讨论】:

      【解决方案5】:

      也许有点太晚了,但我是这样做的:

       // Read the 16 byte iNES header
          char header[16];
          fread( header, 16, 1, file );
      
          // Search for the "NES^Z" signature
          if( memcmp( header, "NES\x1A", 4 ) )
          {
      

      正如 Xeno 建议的那样,使用 memcmp 您不必关心空终止符。毕竟,您并没有真正使用字符串,而更像是 char 数组,由于空终止符,这不一样。由于除了调试之外您实际上不需要打印签名,因此您根本不应该关心使用字符串函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-05-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-24
        • 2019-07-27
        • 1970-01-01
        相关资源
        最近更新 更多