【问题标题】:How to scan the rest of a line in c如何在c中扫描一行的其余部分
【发布时间】:2012-10-23 01:11:12
【问题描述】:

我在一个文件中有几行整数 例如

100 20 300 20 9 45 -1
101 80 80 2 80 2 50 3 70 -1

我想读取前 2 个字符并将它们存储在整数变量中,然后将其余字符存储在一个字符串中,以便稍后进行迭代。

do {
    fscanf(file, "%d %d", &var1,&var2);
    }while(!feof(file));

现在我想扫描该行的其余部分,移动到下一行并重复。但我不确定如何将该行的其余部分扫描成 var3 字符串

..有什么想法吗?

【问题讨论】:

  • 我感觉到了力量的干扰,仿佛有一亿个声音在喊“传入缓冲区溢出”,然后突然沉默了:-)
  • 作为替代方案,您可以将行扫描成字符串,然后使用 subString 方法,将它们保存为两个整数和一个字符串。
  • 你总是可以使用fgets
  • 在使用fscanf 读取整数之后,使用fgets 读取直到行尾。

标签: c


【解决方案1】:

这是你要做的第一件事。除非您完全控制输入数据,否则不要考虑使用 scanf("%s")。否则,您将面临缓冲区溢出。

This answer 展示了一种使用fgets 进行用户输入的安全方式,提供缓冲区溢出检测/避免和行清除,可以轻松适应任何输入流。

一旦您将行(和 整个 行)作为字符串,并且因此知道它可能的最大大小,您可以简单地使用:

char strBuff[1000]. str1[1000]; // Ensure both big enough.
:
// Use /getLine/fgets to get the line into strBuff.
:
int numScanned = sscanf (strBuff, "%d %d %[^\n]", &int1, &int2, str1);

%[^\n] 格式说明符的作用是将任意数量的非换行符扫描成一个字符串:[] 表示一个字符类,^ 表示“匹配所有内容后面的字符,用于(非)匹配的字符是换行符\n

标准引用在此答案的底部(a)


例如,使用那个函数:

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

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Get line with buffer overrun protection.
    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.
    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[strlen(buff)-1] = '\0';
    return OK;
}

使用以下程序:

int main (void) {
    int rc, numScanned, int1, int2;;
    char strBuff[100], str1[100];

    rc = getLine ("Yes> ", strBuff, sizeof(strBuff));
    if (rc == NO_INPUT) {
        // Extra NL since my system doesn't output that on EOF.
        printf ("\nNo input\n");
        return 1;
    }

    if (rc == TOO_LONG) {
        printf ("Input too long [%s]\n", strBuff);
        return 1;
    }

    printf ("OK [%s]\n", strBuff);

    numScanned = sscanf (strBuff, "%d %d %[^\n]", &int1, &int2, str1);
    printf ("numScanned = %d\n", numScanned);
    printf ("int1       = %d\n", int1);
    printf ("int2       = %d\n", int2);
    printf ("str1       = [%s]\n", str1);

    return 0;
}

给出以下输出:

Yes> 100 20 300 20 9 45 -1 blah blah blah
OK [100 20 300 20 9 45 -1 blah blah blah]
numScanned = 3
int1       = 100
int2       = 20
str1       = [300 20 9 45 -1 blah blah blah]

(a) 部分 7.20.6.2 The fscanf functionC11(尽管这与 C99 没有变化)说明了 [ 格式说明符,稍作解释删除不相关的多字节内容:

[ 格式说明符匹配来自一组预期字符(扫描集)的非空字符序列。

对应的参数应该是一个指向一个字符数组的初始元素的指针,该数组的大小足以接受序列和一个终止的空字符,它将自动添加。

转换说明符包括格式字符串中的所有后续字符,直到并包括匹配的右括号 (])。

括号(扫描列表)之间的字符组成扫描集,除非左括号后面的字符是一个抑扬符(^),在这种情况下,扫描集包含所有未出现在扫描列表中的字符在抑扬符和右括号。如果转换说明符以[][^] 开头,则右括号字符在扫描列表中,并且下一个右括号字符是结束规范的匹配右括号;否则后面的第一个右括号字符是结束规范的那个。

【讨论】:

  • @James Famous 遗言。 ;-)
  • 太棒了! [^\n] 到底是做什么的?扫描到行尾?我不熟悉语法
  • @James,如果您可以控制输入,那么您可能不需要我的getLine 函数。但是,我会仍然使用fgets 并检测可能的溢出,因为它更安全。无论如何,其余的答案仍然适用。确保您的缓冲区至少与输入缓冲区一样大,并使用"%d %d %[^\n]\n" 格式字符串。我将在更新中更详细地解释该字符串。
【解决方案2】:

不,您可以使用scanf,前提是您知道缓冲区的大小。您可以避免缓冲区溢出测试它何时发生。恢复逻辑把事情搞砸了,但它仍然是可能的。我建议使缓冲区足够大,以至于溢出确实是一种放弃和死亡的错误。

首先假设一个 256-bye 缓冲区,加上一些其他需要声明的变量。您可以存储的最长字符串为 255 个字节。您可能想要扫描内部空白,但不希望末尾的 \n 换行符成为字符串的一部分。 (在这种情况下,这是fgets 的主要问题。)神奇的序列是:

char var[256], endchar = '\n';
int n;

n = scanf("%255[^\n]%c", var, &endchar);
if ((n < 1) || (endchar!='\n') || ferror(stdin))
{
    if (n==2) { /*it's a buffer overflow*/ }
    else if (n==0 && !ferror(stdin)) { /*must be EOF on 1st byte*/ }
    else { /*an I/O error occurred*/ }
} else { /* OK */ }

这几乎是防弹的,所有的循环都发生在库中。 scanf 格式分解为:

  1. %255[^\n]:最多 255 个字符串,但换行符除外。
  2. %c: 存储下一个字符的单个字符,如果有的话。

返回值是成功存储的字段数。那,endchar 的结束值和ferror() 结果在几个 if 语句中告诉你你需要知道的一切。单个if 检测正常情况。

这允许EOF 在最后一行没有换行符。在这种情况下,feof(stdin) 将为真以供外部循环检测。

PS:反对scanf %s(以及相关的%[])的论据是有根据的,但是如果您可以确保"nnn" 值与缓冲区大小一致,%nnns%nnn[] 是完全安全的。遗憾的是,无法为格式提供计算缓冲区大小。我所知道的最佳选择是使用sprintf() 动态生成scanf() 格式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多