C++读取超过127个ASCII值时cin失败

6
我创建了一个包含256个字符的文本文件,文本文件的第一个字符是ASCII值0,文本值的最后一个字符是ASCII值255。中间的字符从0到255平均递增。所以第27个字符是ASCII值27。第148个字符应该是ASCII值148。
我的目标是读取这个文本文件的每个字符。
我尝试使用cin读取它。我尝试过cin.get()和cin.read(),它们都应该读取未格式化的输入。但是在读取第26个字符时,两者都失败了。我认为当我使用unsigned char时,cin说它正在读取255,这显然是不正确的。当我使用普通的signed char时,cin说它正在读取-1。它应该读取ASCII 26的字符等价物。也许cin认为它已经到达了EOF?但我之前在StackOverflow上阅读过EOF并不是一个实际可写的字符。所以我迷失了,为什么cin会在表示整数-1或整数255的字符值上出错。请问有人能告诉我我做错了什么,为什么以及最好的解决方案是什么,以及为什么吗?
没有太多具体的代码可以粘贴。我尝试了几种不起作用的组合,都涉及到cin.get()或cin.read(),其中包括char或unsigned char,并在它们之间调用char和int的强制转换。我无法读取超过第26个字符,除了这个:
unsigned char character;

while ( (character = (unsigned char)cin.get()) != EOF) { ... }

有趣的是,尽管这不会在第26个字符停止我的while循环,但它也不会继续执行。似乎无论是cin.get()还是cin.read(),只要它检测到不喜欢的东西,它就拒绝前进到下一个字符。我也知道类似于cin.ignore()的东西存在,但我的输入是不可预测的;也就是说,我的文本文件中的这256个字符只是一个测试用例,真正的输入是相当随机的。虽然这是一个更大的作业任务的一部分,但这个具体的问题与该任务无关;我只是卡在了这个过程的一部分上。
注意:我正在从标准输入流中读取,而不是特定的文本文件。看起来仍然没有直接的解决方案。我简直不敢相信这在cin上还没有被做过。
更新:
在Windows上,由于Ctrl-Z的原因,它在第26个字符后停止。我对这个问题并不是那么在意。它只需要在Linux上运行即可。
在Linux上,它可以读取0-127的所有字符。但它似乎没有读取从127到255的扩展ASCII字符。有一个“解决方案”程序,它生成了我们应该模仿的输出,并且该程序能够以某种方式读取所有255个字符。
问题:如何使用cin读取所有255个ASCII字符?
已解决
使用:
int characterInt;
unsigned char character;

while ( (characterInt = getchar()) != EOF )
{
            // 'character' now stores values from 0 - 255
    character = (unsigned char)(characterInt);
}

ASCII 码从 0 到 127。字节值 128 到 255 不属于 ASCII 码,尽管有许多(现在)可怕的编码方式将 ASCII 中的 0-127 取出并用于自己的恶意目的,而将 128-255 占为己有。 - user395760
@delnan 为什么你说它很糟糕?ISO 8859编码在欧洲几乎是普遍的,即使现在也是如此。 (我倾向于使用UTF-8,但在法国和德国仍有许多网站使用ISO 8859-1或ISO 8859-15。请记住,像“isalpha”这样的东西在UTF-8中不起作用。) - James Kanze
@JamesKanze 我之所以说现在的编码方式很糟糕,是因为与Unicode编码不同,你无法使用其中任何一种编码在单个字符串中实际表达许多字符,因为它们彼此不兼容,并且无法可靠地区分它们。我非常清楚其中一些编码方式相当受欢迎(我住在德国),但这并不能使它们更好,只能使它们成为遗留垃圾。我也知道它们在创建时是一个相当合理的解决方案。但是自从一到两十年以来,它们只是编码错误和痛苦的源头,比UTF-8差劣。 - user395760
即使有所有这些Ctrl-Z或文本模式而非二进制模式的问题,为什么read()get()会失败? - Jason
可能是因为您错误地使用了 get()。 正确的用法是:`int character;while ( (character = cin.get()) != EOF) { ... }` - Robᵩ
我想那就是了,没错。最后还用了 getchar() - Jason
3个回答

5
我猜您在使用Windows操作系统。在Windows平台上,字符26代表Ctrl+Z键,用于表示文件的结束,因此iostreams认为您的文件在那个字符处结束了。
这种情况只会在文本模式下发生,而cin正是在使用文本模式。如果您在二进制模式下打开流,则不会出现这种情况。

我正在Windows上编写代码,但程序将在Linux/Unix上运行。 - Jason
@Jason,你会发现在Linux上它的工作方式不同,因为运行时库没有使用这个约定。 - Mark Ransom
是的,我刚刚运行了一个差异比较,输出仍然不理想,但它似乎读入了更多的字符。我需要花更多时间处理细节来尝试解决它。 - Jason

3

std::cin 读取文本流,而不是任意二进制数据。

至于为什么第26个字符很有趣,你可能在使用类似 CP/M 派生系统(如 MS-DOS 或 MS-Windows)。在这些操作系统中,Control-Z 被用作文本文件的 EOF 字符。


编辑: 在 Linux 上,使用 g++ 4.4.3 编译器的以下程序会按预期精确地打印出从0到255(含)的数字:

#include <iostream>
#include <iomanip>

int main () {
  int ch;
  while( (ch=std::cin.get()) != std::istream::traits_type::eof() )
    std::cout << ch << " ";
  std::cout << "\n";
}

这个能在标准输入流上工作吗?我并没有真正读取一个特定的文本文件。 - Jason
1
这可能会有所帮助:https://dev59.com/iGsz5IYBdhLWcg3w5ME0 - Robᵩ
一个有趣的历史课在这里:http://blogs.msdn.com/b/oldnewthing/archive/2004/03/16/90448.aspx - Mark Ransom
如果以二进制模式打开文件,您应该能够读取任何内容。但是一旦文件被打开,您就无法更改模式,并且std::cin是由运行时而不是由您打开的。 - James Kanze
1
@Rob,根据那个链接,一些帖子中说没有解决方案。其中一个帖子的“解决方案”看起来很混乱,似乎使用了新的C++特性?另一个“解决方案”是针对Windows平台的。我想要的只是能够读取所有字符值并感到满意... - Jason
显示剩余2条评论

1
这里有两个问题。第一个问题是在Windows中,cin 的默认模式是文本而不是二进制,导致某些字符被解释而不是输入到程序中。特别是第26个字符Ctrl-Z,由于向后兼容性被解释为文件结尾。另一个问题是由于cin >>的工作方式 - 它会跳过空格。这包括空格,但也包括制表符、换行符等。要从cin读取每个字符,您需要使用cin.get()cin.read()

我正在使用 cin.get()cin.read()。此外,在 Unix 上,它最多读取 127 个字符,而不是更多。你有什么见解吗? - Jason
@Jason,使用od -b命令确保文件包含你认为的字符。我手头没有Unix或Linux系统来评估,但我在Windows上使用Cygwin进行了测试,它可以正常工作。如果需要,我可以将代码编辑到答案中。 - Mark Ransom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接