代码之家  ›  专栏  ›  技术社区  ›  Dimitrie Mititelu

生产线末端奇怪的sscanf行为

  •  -1
  • Dimitrie Mititelu  · 技术社区  · 7 年前

    在为学校做一些项目时,我遇到了以下问题:

    sscanf 读取预期的不同值。

    我想读这样的东西:

    1 0 185336079 0 0 168231418 -256 0 255 1
    2 0 185336079 -256 0 168231418 -256 0 255 2
    3 0 185336079 -256 0 168231418 -256 0 255 3
    4 0 185336079 0 0 0 0 0 255 4
    

    这是我目前使用的代码:

    FILE *fd = open_fd("/proc/firewall", "r");
    
    while ((read = getline(&line, &len, fd)) != -1) {
        sscanf(line, "%d %c %d %d %d %d %d %d %c %c\n", &num,
            &rule_u.inbound_outbound,
            &rule_u.source_ip,
            &rule_u.source_netmask,
            &rule_u.source_port,
            &rule_u.destination_ip,
            &rule_u.destination_netmask,
            &rule_u.destination_port,
            &rule_u.protocol,
            &rule_u.action);
    
        printf("scanf read rule action : %c\n", rule_u.action);
        printf("sscanf whole line:\n%s\n",line);
        convert_rule_from_u();
        print_rule();
    }
    

    上述代码生成的输出如下:

    scanf read rule action : 5
    sscanf whole line:
    1 0 185336079 0 0 168231418 -256 0 255 1
    
    scanf read rule action : 5
    sscanf whole line:
    2 0 185336079 -256 0 168231418 -256 0 255 2
    
    scanf read rule action : 5
    sscanf whole line:
    3 0 185336079 -256 0 168231418 -256 0 255 3
    
    scanf read rule action : 5
    sscanf whole line:
    4 0 185336079 0 0 0 0 0 255 4
    

    预期输出应如下所示:

    scanf read rule action : 1
    sscanf whole line:
    1 0 185336079 0 0 168231418 -256 0 255 1
    
    scanf read rule action : 2
    sscanf whole line:
    2 0 185336079 -256 0 168231418 -256 0 255 2
    
    scanf read rule action : 3
    sscanf whole line:
    3 0 185336079 -256 0 168231418 -256 0 255 3
    
    scanf read rule action : 4
    sscanf whole line:
    4 0 185336079 0 0 0 0 0 255 4
    

    rule_u struct

    typedef struct rule_struct_u {
        unsigned char inbound_outbound;
        unsigned int source_ip;
        unsigned int source_netmask;
        unsigned int source_port;
        unsigned int destination_ip;
        unsigned int destination_netmask;
        unsigned int destination_port;
        unsigned char protocol;
        unsigned char action;
    } rule_struct_u;
    

    我做错了什么?

    3 回复  |  直到 7 年前
        1
  •  2
  •   chux    7 年前

    代码在每行的末尾都有问题,无法读取 "255 0" 使用格式说明符 "%c %c" .

    这将扫描并保存字符 '2' 和和第一个 '5' . OP希望保存为所需结果为255和0的整数。

    使用 "%hhu %hhu" 在中读取数字文本并将其另存为整数 unsigned char .

        2
  •  1
  •   Jonathan Leffler    7 年前

    通常,在格式字符串中为 scanf() 函数族是个坏主意,请参见 Trailing blank in scanf() format string 了解更多详细信息。如果函数为 sscanf() 或者它的一个亲戚,从字符串而不是文件流中读取,那么这不是一场灾难。

    然而,您也遇到了第二个问题 %c 最后不是读所有的 255 你需要一个 %d (或者可能 %hhd %hhu ),并应查看所有其他%c列;也许他们也应该是数字?

    所以你说应该是这样的: sscanf(line, "%d%hhu%d%d%d%d%d%d%hhu%hhu",... 可以吗?我想它读了全部 255 之前我在玩弄它,它正在发挥作用,或者至少看起来是这样。

    转换规范之间的间距正常;它们使它更具可读性。字符串末尾的换行符表示“读取空白,直到找到非空白的内容”,如果输入来自(磁盘)文件或字符串,则可以,但如果输入来自终端、管道、FIFO、套接字,则会导致灾难。 打电话给 scanf() 直到输入端出现下一条消息时才会返回;它需要的不是换行符、空格或制表符,而是说“就这些”。那可能不是你想要的。

    示例数据上的最后一个字段显然不是单个字符。您可能需要一个适合无符号字符的数字 %hhu 是适当的。另外两个 %c类 列在数据中只包含零,但如果它们可以包含大于9的数字,则需要 %hhu .

    “入站/出站”列很可能是0或1,因此 %c类 这可能还可以(但请注意,值将为 '0' '1' 0 1 !). 您知道通话中的数据(或需要知道数据)。

    我会注意到你的结构浪费了相当多的空间。开头的一个字节字段后面有3个字节的填充,结尾还有两个字节的填充。考虑移动彼此相邻的三个无符号字符字段,以在每个结构中节省4个字节(仍然有1个填充字节,但这是对5的改进)。想想这是否重要(可能并不重要)。

    此外,您还应该测试 sscanf() scanf() 以确保输入正确。

    if (sscanf(line, "%d %hhu %d %d %d %d %d %d %hhu %hu", &num,
        &rule_u.inbound_outbound,
        &rule_u.source_ip,
        &rule_u.source_netmask,
        &rule_u.source_port,
        &rule_u.destination_ip,
        &rule_u.destination_netmask,
        &rule_u.destination_port,
        &rule_u.protocol,
        &rule_u.action) != 10)
    {
        …handle error…do not pass go, do not collect $200…
    }
    
        3
  •  0
  •   Dúthomhas Rishu    7 年前

    倒数第二个转换说明符是 %c ,它只需要一个字符。您的输入有3个: "255" (或类似的)。

    因此,您正在偏离正轨,每一行从最后一行读取的项目比之前的迭代多一个。