为啥我不能使用 ifstream 读取撇号而不崩溃?

Posted

技术标签:

【中文标题】为啥我不能使用 ifstream 读取撇号而不崩溃?【英文标题】:Why can't I read apostrophes using ifstream without it crashing?为什么我不能使用 ifstream 读取撇号而不崩溃? 【发布时间】:2017-11-25 20:05:09 【问题描述】:

我正在使用此代码:

std::string word;
std::ifstream f((file_name + ".txt").c_str());
while (f >> word) 
    good_input = true;
    for (int i = 0; i < word.length(); ++i) 
        if (ispunct(word.at(i))) 
            word.erase(i--, 1);
        
        else if (isupper(word.at(i)))
            word.at(i) = tolower(word.at(i));
        
    

每次我从文本文件中读取“不”这个词时,都会收到以下错误:

调试断言失败! 程序:目录\SortingWords(Length).exe 文件:minkernel\crts\ucrt\src\appcrt\convert\istype.cpp 线路:36 表达式:c >= -1 && c 欲了解更多信息,请访问... [等]

当我点击“中止”时,我的程序以代码 3 退出。不知道这是否有帮助?

看起来它可能与撇号有关?此代码适用于在我的文档中查找所有其他单词,直到这个。并且适用于不包含撇号但包含大量其他标点符号的文档...

我尝试更改文本文件的编码(仅使用记事本制作),但这没有帮助。通常发现很多关于撇号的投诉,但没有有效的答案。谁能帮我弄清楚发生了什么?

【问题讨论】:

我的猜测是这是一个聪明的报价,但我无法告诉。 如果您在十六进制编辑器中查看文件,撇号是否只占用一个字节?它是您期望的编码值吗?如果您在调试器中捕捉到崩溃,它真的是您期望的地方吗(即在您向我们展示的代码段中)?所涉及的变量的值是多少?他们是你所期望的吗? 我尝试简单地逐字重写我的文件,而不是从原始来源复制和粘贴(就像我之前所做的那样)。很痛苦,但它已经成功了,现在正在处理各种标点符号和奇怪的情况。很抱歉浪费了您的时间!看起来使用的字符有问题。否则,只是为了满足任何好奇心,是的,错误正是在这里发生的(使用断点找到)并且变量看起来都很好! 顺便说一句,您应该不需要使用自己的断点来查找崩溃位置。 VS 应该在触发断言时在调试时自动中断,让您进入您关心的堆栈帧,以查看究竟是哪个调用导致了它以及程序的状态。 【参考方案1】:

正如documentation for ispunct 所说:

如果 ch 的值不能表示为,则行为未定义 unsigned char 不等于 EOF

如果您链接到调试运行时,Visual C++ 足以为该错误添加几乎明确的消息(这通常是未定义行为的情况 - 对于发布运行时,它只是崩溃或行为异常;对于调试运行时,你会得到一个错误对话框)。

理论上,这意味着在您的环境使用的字符集中,' 不能表示为unsigned char,即它的字符代码太大或太低。

实际上,这在 Windows 上似乎不太可能,甚至可能是不可能的。更有可能的是,您的文件实际上并不包含撇号,而是一个仅 看起来 像一个字符的字符,例如口音:´

您可以通过以下简单方式重现问题:

#include <ctype.h>

int main()

    ispunct('\'');
    ispunct('´'); // undefined behaviour (crash or error message with Visual C++)

isupper 也有同样的问题。

您可以通过static_cast 安全地使用这些功能,例如:

if (ispunct(static_cast<unsigned char>(word.at(i))))

当然,现在ispunct 将为角色返回零。如果你真的需要覆盖´,你必须明确地这样做,例如使用这样的辅助函数:

bool extended_ispunct(int c)

    return static_cast<unsigned char>(c) || c == '´';

【讨论】:

顺便说一下,cout &lt;&lt; ((char)'´' == '´') 在this online MSVC compiler 上打印 0。 @chris:添加/W4。它会告诉你原因:warning C4310: cast truncates constant value。事实上,´ 在我的机器上是 498440。 @chris:没错,那是我函数中的一个错误! :) 感谢您发现它,让我修复它...

以上是关于为啥我不能使用 ifstream 读取撇号而不崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在使用 ifstream 通过命名管道读取一行后得到 EOF?

为啥撇号会使 Rails 5.1.5 请求解析崩溃?

在 package.json 和 package-lock.json 中更新版本号而不更新依赖

撇号导致 Node SQL 应用程序崩溃

当应用程序名称包含撇号和空格时符号化崩溃日志(已部署的应用程序)

svn - 基于文件的修订号而不是 repo