【问题标题】:Sscanf not behaving as intended in extracting stringsSscanf 在提取字符串时未按预期运行
【发布时间】:2013-11-10 22:01:56
【问题描述】:

我有一个 SPARQL 查询,例如“select ?Y ?Z where ...”。我要做的就是提取变量“?Y”和“?Z”。我写了以下循环:

char *p = "select ?Y ?Z where condition";
char *array[2];

p += strlen("select");        /* advancing the pointer */

for(int i = 0; i < 2; i++)
{
        sscanf(p, "%m[?a-zA-Z] %[^\n]", array[i], p);    /* 'm' tells sscanf to perform dynamic memory allocation */
        printf("%s, ", array[i]);
        printf("%s, ", p);
}

但是,我没有得到预期的行为。 array[1] 包含垃圾,array[2] 包含 null。

【问题讨论】:

  • 可能与打开的/*有关
  • 打开是什么意思?
  • 请在将指针前进到位置后检查

标签: c scanf


【解决方案1】:

基于http://man7.org/linux/man-pages/man3/scanf.3.html
例如在哪里有

char *p;
n = scanf("%m[a-z]", &p);

我认为你应该改变你的

sscanf(p, "%m[?a-zA-Z] %[^\n]", array[i], p);

sscanf(p, "%m[?a-zA-Z] %[^\n]", &array[i], p);

看看%m 是如何与char ** 配对而不是char*

我也建议阅读THIS 讨论。

我还注意到您的 pchar * 并且您使用字符串常量对其进行初始化,然后尝试通过调用 sscanf 来覆盖它。

除此之外,您在同一函数调用中读取和写入内存p 指向。我在这里不是 100% 确定,但这可能是未定义的行为。

希望对你有帮助。

【讨论】:

    【解决方案2】:

    你有几个问题。 zubergu 在他的answer 中正确诊断出两个:(1)传递一个char *,其中需要一个char **(参见scanf()),以及(2)试图覆盖您正在扫描的字符串首先是只读的,并且在任何情况下都会调用未定义的行为。

    另一个是你没有检查来自sscanf()的返回状态。你可能需要这样的东西:

    char *p = "select ?Y ?Z where condition";
    char *array[2];
    
    p += strlen("select");
    
    for (int i = 0; i < 2; i++)
    {
        int offset;
        if (sscanf(p, " %m[?a-zA-Z]%n", &array[i], &offset) != 1)
            ...report error and break loop...
        printf("%s, ", array[i]);
        p += offset;
    }
    

    注意转换规范前的空白以跳过前导空格。

    如果您有支持该符号的sscanf() 版本,则此代码应该可以正常工作:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void)
    {
        char *p = "select ?Y ?Z where condition";
        char *array[2] = { 0, 0 };
        int i;
        int j;
    
        p += strlen("select");
    
        for (i = 0; i < 2; i++)
        {
            int offset;
            printf("p = <<%s>>\n", p);
            if (sscanf(p, " %m[?a-zA-Z]%n", &array[i], &offset) != 1)
                break;
            printf("%d: <<%s>> (offset = %d)\n", i, array[i], offset);
            p += offset;
        }
        printf("%d: all done\n", i);
    
        for (j = 0; j < i; j++)
            free(array[j]);
        return 0;
    }
    

    Mac OS X 10.9 不支持m 修饰符;旧版本的 Linux 也没有(自我注意:必须更新可用的虚拟机)。在 Ubuntu 12.04 衍生版本上进行测试时,我得到了输出:

    p = << ?Y ?Z where condition>>
    0: <<?Y>> (offset = 3)
    p = << ?Z where condition>>
    1: <<?Z>> (offset = 3)
    2: all done
    

    【讨论】:

    • 非常感谢您的大力帮助。我认为格式说明符 %n 填充了读取的字节数。
    • 是的; %n 报告在处理转换规范时读取的字节数。但是,对于返回值,它不算作转换之一(它总是成功)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-02
    • 1970-01-01
    • 1970-01-01
    • 2015-12-14
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    相关资源
    最近更新 更多