我如何在 c++ STL 文件读取期间可移植地捕获和处理 UTF "EN DASH" 缺点?

How can I Portably Catch and Handle UTF "EN DASH" Minuses During c++ STL File Reading?

我正在维护一个大型开源项目,所以我在 I/O 前面遇到了一个奇怪的边缘案例。

当我的应用程序解析包含如下文本行的用户参数文件时:

CH3 CH2 CH2 CH2     −68.189775    2    180.0              ! TraPPE 1

...起初它看起来很无辜,因为它是按需要格式化的。但后来我看到减号是一个 UTF 字符 () 而不是 (-)。

我只是将 STL>>ifstream 对象一起使用。

当它尝试转换为负数并在 UTF 字符 STL 上失败时,显然只是将内部标志设置为 "bad",这触发了我停止读取过程的逻辑。这很好,因为如果没有这种逻辑,我将很难找到它。

但这绝对不是我想要的错误处理。我想在用 >> 读取 double 时捕获常见的减号字符,如果字符串是格式正确的负数,则替换它们并完成转换。

这似乎发生在我的用户身上相对频繁,因为他们从程序(计算器或 Excel 也许在 Windows 中?)复制和粘贴以获取他们的文件值。

我有点惊讶没有在 Stack Overflow 上发现这个问题,因为它似乎无处不在。我在这个问题上找到了一些参考:

c++ error cannot be used as a function, some stray error [closed]

...但这是一个略有不同的问题,其中代码包含那种相似但不兼容的 "minus-like" EN DASH UTF 字符。

有没有人有一个很好的解决方案(最好是紧凑、便携和可重复使用的)来在读取双精度数或有符号整数时捕获这些糟糕的缺点?

注:
不管你信不信,我不想使用 Boost 或 c++11,我在某些超级计算机上的一些用户无法访问这些库。我尽量保持它的便携性。

可能正在使用适合您的自定义 std::num_get。其他字符值方面也可以被覆盖。

#include <iostream> 
#include <string> 
#include <sstream> 

class num_get : public std::num_get<wchar_t> 
{ 
public: 
    iter_type do_get( iter_type begin, iter_type end, std::ios_base & str, 
                      std::ios_base::iostate & error, float & value ) const 
    { 
        bool neg=false; 
        if(*begin==8722) { 
            begin++; 
            neg=true; 
        } 

        iter_type i = std::num_get<wchar_t>::do_get(begin, end, str, error, value); 

        if (!(error & std::ios_base::failbit)) 
        { 
            if(neg) 
                value=-value; 
        }    
        return i; 
    } 
}; 

int main(int argc,char ** argv) {  

    std::locale new_locale(std::cin.getloc(), new num_get); 

    // Parsing wchar_t streams makes live easier but in principle
    // it should work with char (e.g. UTF8 as well)

    static const std::wstring ws(L"CH3 CH2 CH2 CH2     −68.189775    2    180.0              ! TraPPE 1"); 
    std::basic_stringstream<wchar_t> wss(ws);                                                                 
    std::wstring a; 
    std::wstring b; 
    std::wstring c; 
    float f=0; 

    // Imbue this new locale into wss 
    wss.imbue(new_locale);                 

    for(int i=0;i<4;i++) { 
        std::wstring s; 
        wss >> s >> std::ws; 
        std::wcerr << s << std::endl; 
    } 

    wss >> f;

    std::wcerr << f << std::endl; 
}

除非手动,否则不会发生。 Unicode 中有 许多 个字符,有一个 Em Dash 和一个 En Dash,很可能还有更多。例如,您是否考虑过 Em Dash 的可能性,然后是不间断的-space,然后是一些数字?还是 RTL 覆盖? Unicode 是传奇,因为可能性几乎是无限的,而在 C++ 中是双重传奇,因为对它的标准支持可以慈善地描述为 ISIS 对理智的支持。

做到这一点的唯一真正方法是找到用户报告的每种情况,然后手动处理 - 即不要使用 operator>> 作为双倍。