解析大型整数输入时 C++ 输入/输出中的奇怪行为

Strange Behavior in C++ Input/Output When Parsing Large Integer Input

提问人:Richard Wang 提问时间:12/31/2022 更新时间:12/31/2022 访问量:141

问:

我有以下一段代码:

#include <iostream>
using namespace std;

int main() {
  // Number of inputs
  int N;
  scanf("%d", &N);
  // Takes in input and simply outputs the step it's on
  for (int i = 0; i < N; i++) {
    int Temp;
    scanf("%d", &Temp);
    printf("%d ", i);
  }
}

当接收大量整数输入时,C++ 会停在打印输出的某个点,似乎在等待更多输入的到来。 给定输入 2049 个 1,程序在打印 2048 个整数(0 到 2047)后停止,并且不打印最终的 2048(第 2049 个整数)。2048 看起来很可疑,是 2 的幂。 似乎情况是,输入值越大,程序决定停止的速度就越快,在这种情况下,在看起来像是随机步骤之后。例如,我给了它 991 个整数(最多一万个),程序在迭代 724 后停止输出。 请注意,我将数字复制并粘贴为一个整体块,而不是一个接一个地输入和输入它们,但我怀疑这起了作用。我也尝试过 cin 和 cout,但它们没有帮助。 有人可以解释一下这种现象背后的原因吗?

C++ 输入 IO 数字 输出

评论

0赞 Captain Obvlious 12/31/2022
您是否 1) 检查以确保您粘贴的数据不包含任何不应该存在的字符,2) 尝试通过管道传输数据,以及 3) 检查返回的值以确保它读取数据?圣诞老人会检查他的列表两次,因此当返回值可以指示错误时,您应该检查返回值scanf
0赞 Nathan Pierson 12/31/2022
“但我怀疑这起了作用”。好吧,也许有,也许没有。如果你确定,你可能不会问这个问题。最小可重现示例的要点是找到您的代码和输入的版本,其他人可以使用该版本来遇到您遇到的相同问题。如果人们必须根据对事态的粗略自然英语描述来猜测您的输入可能会发生什么,那么调试起来就困难得多。
2赞 Mark Ransom 12/31/2022
在 C++ 中使用和有点不寻常。没有多少人会在这方面有丰富的经验。scanfprintf
1赞 Peter 12/31/2022
尽管您声称“无关紧要”,但很有可能“整个数字块”包含一些垃圾字符。读取数字输入(是否流式处理)时,此类字符通常会导致流处于错误状态,并且(更重要的是)该字符会留在 I/O 缓冲区中,循环中的后续读取操作将反复遇到它。尝试检查调用的返回值 - 如果成功,它将返回,返回表示扫描失败,表示另一个错误scanf()cinscanf()10EOF
1赞 user4581301 12/31/2022
不检查和处理错误条件最终会导致调试浪费时间。始终检查和处理返回代码。

答:

-1赞 spencer741 12/31/2022 #1

我的第一个想法是,你在输入端遇到了某种障碍......

命令行长度的限制[通常]不是由 shell 强加的,而是由操作系统强加的。

Bash 命令行和输入限制 - SO

然而,情况可能并非如此。

首先,专注于您的数据,确保您的数据是您认为的(没有意外字符),然后尝试调试您的读取,确保这些值像您预期的那样进入内存。

尝试将读取和写入分成两个循环,这可能会根据您的技能水平帮助您更轻松地进行调试,但同样,请确保您的读取不会发生一些时髦的事情。对这个的阅读高度怀疑......

下面有几个裂缝......还没测试过。希望这有帮助!

#include <iostream>

int main() {
  int N;
  std::cin >> N;

  // Read N integers, storing to array
  int* numbers = new int[N];
  for (int i = 0; i < N; i++) {
    std::cin >> numbers[i];
  }

  // Print
  for (int i = 0; i < N; i++) {
    std::cout << numbers[i] << " ";
  }
  std::cout << std::endl;

  // Free the dynamically allocated memory
  delete[] numbers;

  return 0;
}

好。。。也许更优化一点......

#include <iostream>

int main() {

  int N;
  std::cin >> N;

  // fixed-size on the stack
  int numbers[N];

  // cin.tie(nullptr) and ios::sync_with_stdio(false) might improve perf.
  std::cin.tie(nullptr);
  std::ios::sync_with_stdio(false);

  // Read N integers, storing to array
  for (int i = 0; i < N; i++) {
    std::cin >> numbers[i];
  }

  // Print
  for (int i = 0; i < N; i++) {
    std::cout << numbers[i] << " ";
  }
  std::cout << std::endl;

  return 0;
}

评论

5赞 Captain Obvlious 12/31/2022
首先,没有理由使用,在这里,当或至少是更好的选择。其次,尽管某些编译器可能支持它,但它不是有效的 C++(另外,如果 的值足够大,您将耗尽系统堆栈)。newdeletestd::vectorstd::make_unique<int[]>int numbers[N];N
0赞 spencer741 12/31/2022
@CaptainObvlious 好点!感谢您的反馈。
2赞 Richard Wang 12/31/2022 #2

我已经找到了问题的答案。失败背后的原因确实是由于复制和粘贴了大量输入,正如许多人所建议的那样,我感谢大家的帮助。但是,没有不正确的字符,导致此问题的原因是规范模式造成的 4096 个字符限制。

在规范模式下,终端允许用户使用箭头键、退格键等导航输入。仅当存在换行符或缓冲区已满时,它才会将文本发送到处理器。这个缓冲区的大小是 4096 个字符,很明显为什么代码无法解析比这更多的输入,即 2049 s 是 4098 个字符。可以切换到非规范模式,该模式允许更大的输入,但代价是无法导航它,使用 .输入后,它又回到了规范模式。"1 "stty -icanonstty icanon

实际上,用换行符分隔数字输入输入似乎是最简单的解决方法。

这个来源对我很有帮助:http://blog.chaitanya.im/4096-limit。 这篇关于 unix 堆栈交换的帖子与我的问题类似:https://unix.stackexchange.com/questions/131105/how-to-read-over-4k-input-without-new-lines-on-a-terminal