【问题标题】:sscanf multiple format specifiers to single arraysscanf 多个格式说明符到单个数组
【发布时间】:2022-01-02 10:00:22
【问题描述】:

是否可以使用多个格式说明符运行sscanf,每个格式说明符将在数组中设置一个项目?目前,我必须分别指定指向每个数组槽的指针,感觉非常重复:

unsigned int buffer[8];
sscanf(
        line,
        "%6o %6o %6o %6o %6o %6o %6o %6o",
        buffer + 0,
        buffer + 1,
        buffer + 2,
        buffer + 3,
        buffer + 4,
        buffer + 5,
        buffer + 6,
        buffer + 7);

我正在寻找类似于 JavaScript 的 spread operator 或 Python 的 *-operator for unpacking argument lists 的东西:

sscanf(
        line,
        "%6o %6o %6o %6o %6o %6o %6o %6o",
        ...buffer);

目前,此操作失败并出现以下错误:

file.c:42:21: error: format ‘%o’ 需要一个匹配的 ‘unsigned int *’ 参数 [-Werror=format=]

这是意料之中的。如何告诉sscanf 将每个格式说明符值放入数组中的连续槽中?是否可以?如果没有,是否有聪明的解决方法(hack)?

【问题讨论】:

  • "如何告诉 sscanf 将每个格式说明符值放入数组中的连续槽中?有可能吗?"不,这是不可能的。
  • knittl,当文本输入为 negative 时会发生什么,例如"-1 -2 -3 ..."。这是可以接受的还是错误的?
  • @chux-ReinstateMonica 文件/字符串永远不会包含负数。如果是这样,我无法处理它。因此出错就可以了。

标签: arrays c pointers scanf


【解决方案1】:

如何告诉 sscanf 将每个格式说明符值放入数组中的连续槽中?是否可以?如果没有,是否有聪明的解决方法(hack)?

使用循环。

健壮的代码还会检测line 是否不包含 8 组八进制数字文本。


strtol() 循环的替代方法是 sscanf()"%6o %n"

" %n" 扫描可选的空白,然后保存扫描的偏移量。

size_t n = sizeof buffer / sizeof buffer[0];  // 8

const char *p = line;
size_t i;
for (i = 0; i < n; i++) {
  int n = 0;
  if (sscanf(p, "%6o %n", &buffer[i], &n) != 1) {  // or use strtol()
    Handle_scanf_failure(); // Add user code here to cope with this error
  }
  p += n; // Advance p to the next part of the buffer
}
if (*p != '\0') {
  Handle_junk_at_end_of_buffer(); // Add user code here to cope with this error
}

strtol()"%o" 更好地处理溢出,但由于我们的最大 宽度 为 6 个八进制数字,这将防止 32 位 unsigned 溢出。

【讨论】:

    【解决方案2】:

    如何告诉sscanf 将每个格式说明符值放入数组中的连续槽中?有可能吗?

    根据scanf 没有这样的转换说明符。

    如果没有,是否有聪明的解决方法(hack)?

    是的,一次循环并提取一个。 strtoul 在这种情况下可能有用。

    例子:

    #include <errno.h>
    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    size_t scan_uint_array(const char *line, unsigned int buffer[], size_t count, int base)
    {
        char *str_end;
        for(size_t i = 0; i < count; ++i) {
            unsigned long tmp = strtoul(line, &str_end, base);
            if(str_end == line) return i; // no conversion done
            if(errno == ERANGE) return i; // out of range for ULONG
            if(tmp > UINT_MAX) {          // out of range for UINT
                errno = ERANGE;
                return i;
            }
            buffer[i] = tmp; // all good, save this
            line = str_end;  // move line to where the next scan should be done
        }
        return count;
    }
    
    #define Size(x) (sizeof (x) / sizeof *(x))
    
    int main() {
        unsigned int buffer[8];
        size_t ex = scan_uint_array(" 1 2 3 4 5 6 7 ", buffer, Size(buffer), 8);
        
        printf("extracted=%zu wanted=%zu\n", ex, Size(buffer));
        
        for(size_t i = 0; i < ex; ++i)
            printf("%u ", buffer[i]);
    }
    

    输出

    extracted=7 wanted=8
    1 2 3 4 5 6 7 
    

    【讨论】:

    • 与往常一样,C 迫使您花费数小时重新发明其他语言免费提供给您的内容,最终得到一个较慢、不太通用的解决方案,该解决方案通常在其第一次迭代中存在错误并添加样板没有人真正感兴趣的代码。从 C# 回到 C 甚至 C++ 对我来说总是很清醒。生产力似乎变慢了。
    • @Peter-ReinstateMonica 啊,但它的基本性和适应性通常导致使用 C 作为新机器的首批目标之一,从而允许快速使用数十年的现有代码。我们可以讨论 C 语言的优缺点,但这不是讨论的地方。可以说所有语言都有其 +/-。
    • @chux-ReinstateMonica 当然可以。我就是忍不住要做出这样的观察。
    猜你喜欢
    • 1970-01-01
    • 2017-06-14
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    • 2014-03-04
    • 2014-06-22
    • 2014-06-27
    • 1970-01-01
    相关资源
    最近更新 更多