【问题标题】:fscanf won't read first variable in each linefscanf 不会读取每行中的第一个变量
【发布时间】:2014-05-26 19:57:39
【问题描述】:

我正在尝试从文件中读取信息,但我的代码不会读取每一行中的第一个输入。

这是我的代码:

#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;

int main()
{
FILE* inFile;
long long int time;
char state, ID[12], name[51];

inFile = fopen("file.i2", "r");

while (fscanf(inFile, "%i %s %s %[^\n]s",&time ,&state, &ID, &name)!=EOF)
        cout << time << ' ' << state << ' ' << ID << ' ' << name << endl;

return 0;
}

这是输入:

1111 A 01010112345 SomeString1
1112 A 01010154321 SomeString2
1113 A 11111122222 SomeString3
1114 B 12121233333 SomeString4
1115 B 12121233333 SomeString5

这是输出:

1024 A 01010112345 SomeString1
1024 A 01010154321 SomeString2
1024 A 11111122222 SomeString3
1024 B 12121233333 SomeString4
1024 B 12121233333 SomeString5

在我的情况下,输出应该与输入相同。

【问题讨论】:

  • %s 将写入一个以 null 结尾的字符串,从而导致未定义的行为,因为您没有提供足够的存储空间。
  • 您可能不想开始对您传递的类型撒谎:%i 用于int,而不是long long int。如果你想阅读long long int,你会使用lli。由于您将此问题标记为 C++,因此我建议使用std::ifstream,因为它会自动计算出必要的转换。此外,您应该检查4 而不是EOF:只有当流甚至无法读取一个元素时才会返回常量EOF
  • @DietmarKühl 我将 %i 切换为 %lli 并将 while 条件更改为 '... == 4',但程序仍然返回时间为 1024。
  • @KerrekSB 你能解释一下我没有提供足够的存储空间吗?
  • @user3677314 这应该是字符状态[2],'A' + '\0' 是2个字符,你为什么不看下面的答案?

标签: c++ file scanf


【解决方案1】:

当使用 %s 格式时,您的状态很小(null 终止字符串),因此您有未定义的行为。另外你想使用%Ld 格式读入long long integer。

long long int time;
char state[2], ID[12], name[51];
           ^ 
//         make room for '\0'


while ( fscanf( inFile, "%Ld %1s %11s %50[^\n]s",&time ,&state, &ID, &name) != 4)
        cout << time << ' ' << state << ' ' << ID << ' ' << name << endl;

但是,您应该使用std::ifstream。这样,您在变量转换方面的问题就会减少。请注意,我们现在指定 scanf 读取的字符数(%2s、%12s、%51s),现在我们测试 fscanf 的 == 4,即成功读取元素的数量,因为 scanf 的返回值是

成功分配的接收参数数,如果读取则为 EOF 在分配第一个接收参数之前发生故障。

http://en.cppreference.com/w/c/io/fscanf

【讨论】:

  • 谢谢。我忘记了“状态”中的结束符号。
  • 据我了解,%2s 是 UB。应该是%1s。不包括空终止符。
【解决方案2】:

您对fscanf() 的尝试在多个方面遭到破坏:

  1. 要读取long long int,您需要使用长度修饰符ll。您传递了long long int 的地址,其中预计会出现int。现在一切都是未定义的行为。
  2. 要读取一个字符,格式说明符是%c 而不是%s
  3. 虽然不是严格错误,但字符串的大小并未限制为可用长度:为避免由于缓冲区溢出导致的漏洞,您应该指定最大大小。
  4. fscanf() 返回成功转换的次数。仅当没有任何转换成功时才会返回结果 EOF,即您应该针对 4 而不是 EOF 进行测试。

使用转换

while (fscanf(inFile, "%lli %c %s %[^\n]s",&time ,&state, &ID, &name) == 4) {
    cout << time << ' ' << state << ' ' << ID << ' ' << name << '\n';
}

应该可以正常工作。我建议使用流作为输入,因为它们会自动进行转换:

std::ifstream in("file.i2");
while ((in >> time >> state >> std::setw(12) >> ID).getline(name, 51)) {
    cout << time << ' ' << state << ' ' << ID << ' ' << name << '\n';
}

虽然在读取字符串时仍然需要设置大小,但这样不易出错。

【讨论】:

    猜你喜欢
    • 2016-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    • 2011-07-09
    相关资源
    最近更新 更多