【问题标题】:Using sscanf to parse a long line of words on a single line使用 sscanf 解析单行的长行单词
【发布时间】:2020-02-12 22:40:31
【问题描述】:

在开发一个小程序来扫描关键数据项的英文单词行时,我选择了 sscanf() 来解析该行。由于每行存在未知数量的单词,因此必须使用请求中指定的最大可能字段数调用 sscanf()。这会导致一个又长又丑的单行语句。一种更简洁的技术是使用 sscanf() 在编程循环中一次获取一个单词。不幸的是,无法知道 sscanf() 跳过了多少空格来获取下一个字段。因此,不可能使用一个字符串指针再次调用 sscanf(),该字符串指针反映了 sscanf() 在上一次调用中停止的确切位置。代码示例如下。两个问题:1)我在使用 sscanf() 时是否遗漏了什么? 2)在c中有更好的方法吗?

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

/*
 * using sscanf to parse a line (null terminated string) with fields (words)
 * separated by one or more spaces into an array of words (fields).
 */

void main()
{
        int     i,j;
        int     idx;
        char    string[100] = "word1 word2  word3  word4    word5    word6  word7\0";
        char    fields[20][10];
#if 1
        j=sscanf (&string[0], "%s%s%s%s%s%s", &fields[0][0], &fields[1][0], &fields[2][0], &fields[3][0], &fields[4][0], &fields[5][0]);
        printf("sscanf returned: %d\n",j);
#else
/*
 *  this would be the preferred way to parse a long line of words,
 *  but there is no way to know with certainty how many spaces sscanf
 *  skipped over to obtain the next string (word). A modified version
 *  of sscanf that either modified an integer pointer argument or
 *  updated the pointer to the input string (line) would allow
 *  subsequent calls to pick up where the last sscanf call left off.
 *
 */
        for (i=0,idx=0;i<6;i++){
                j=sscanf (&string[idx], "%s", &fields[i][0]);
                idx += strlen(&fields[i][0]);
                printf("sscanf returned: %d\n",j);
                if (j==0)
                        break;
        }
#endif

        for (i=0;i<6;i++){
                printf("%s",&fields[i][0]);
        }
        printf("\n");
        return;
}

【问题讨论】:

  • 解决方案就像一个魅力。谢谢你。自 2001 年以来,我一直退出编码行业。我值得信赖的旧 c 参考书,Harbison & Steels 的“c - A Reference Manual (1987)”需要与我一起退休。

标签: c scanf extract c-strings word


【解决方案1】:

在用作初始化器的字符串文字中

char    string[100] = "word1 word2  word3  word4    word5    word6  word7\0";

显式终止零是多余的。除了显式终止零之外,字符串文字已经包含终止零。

你来了。

#include <stdio.h>

int main(void) 
{
    char    string[100] = "word1 word2  word3  word4    word5    word6  word7";
    char s[10];

    const char *p = string;

    for ( int n = 0; sscanf( p, "%s%n", s, &n ) == 1; p += n )
    {
        puts( s );
    }

    return 0;
}

程序输出是

word1
word2
word3
word4
word5
word6
word7

另一种方法是使用标准函数strtok 或一对函数strcspn 和strspn。

例如

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

int main(void) 
{
    char    string[100] = "word1 word2  word3  word4    word5    word6  word7";

    const char *delim = " \t";

    const char *p = strtok( string, delim );
    while ( p != NULL )
    {
        puts( p );
        p = strtok( NULL, delim );
    }

    return 0;
}

程序输出同上图。

这是一个使用标准函数strcspn和strspn的演示程序。

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

int main(void) 
{
    char    string[100] = "word1 word2  word3  word4    word5    word6  word7";

    const char *delim = " \t";

    for ( const char *p = string; *p; )
    {
        p += strspn( p, delim );

        const char *q  = p;

        p += strcspn( p, delim );

        int n = p - q;

        if ( n ) printf( "%*.*s\n", n, n, q );
    }

    return 0;
}

同样的输出将与上面显示的相同。

请注意,在这种情况下,提取的单词不是以零结尾的。因此,要将它们作为字符串复制到字符数组中,您应该使用memcpy,然后将复制的字符附加到终止零。

【讨论】:

  • 感谢编码示例。第一个是最优雅的我在 1990 年代是一个不错的 c 程序员。工作了 8 年的 6 个月 unix 支持合同。我发现在这个领域闲置了 20 年,导致我忘记的比我记得的要多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-23
  • 1970-01-01
  • 2013-03-21
  • 1970-01-01
  • 2014-06-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多