【问题标题】:How can I Portably Catch and Handle UTF "EN DASH" Minuses During c++ STL File Reading?在 c++ STL 文件读取期间,如何可移植地捕获和处理 UTF“EN DASH”减号?
【发布时间】:2015-02-28 16:42:44
【问题描述】:

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

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

CH3 CH2 CH2 CH2     −68.189775    2    180.0              ! TraPPE 1

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

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

当它尝试转换为负数并在 UTF 字符上失败时,显然只是将内部标志设置为“坏”,这触发了我停止读取过程的逻辑。这有点好,因为如果没有这种逻辑,我会更难追踪它。

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

我的用户似乎相对频繁地发生这种情况,因为他们从程序(可能是 Windows 中的计算器或 Excel?)复制和粘贴以获取他们的文件值。

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

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

...但这是一个稍微不同的问题,其中代码包含那种相似但不兼容的“负号”EN DASH UTF 字符。

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

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

【问题讨论】:

  • 将每一行一次读入一个字符串,应用任何修复,然后分割字符串(使用stringstream、regex 或其他任何有效的方法)。
  • 当然,但这很不具体...我已经知道我可以将它读到string,然后使用替换该字符的代码...fileformat.info/info/unicode/char/2013/index.htm ...也许使用包装器来包装 ifstream 来捕捉这种情况?无论如何,我想看看是否有人有实际的代码来处理这个......你的回答,很感激,但它基本上就是我所在的地方。我把它放在这里是希望有些人已经解决了这个问题并有一个最佳实践/便携/紧凑的解决方案。
  • 您粘贴到问题中的字符不是 u+2013,而是 u+2212。您可能需要针对多种可能性进行编码。
  • 另外,C++11 不是库。无论如何,您都可以简单地静态链接所需的库并将它们与您的应用程序一起发布。不过在这里不会有任何区别。
  • P.S.如果您正在寻找有类似问题的其他人,请搜索“智能引号”。

标签: c++ stl io signed utf


【解决方案1】:

可能正在使用适合您的自定义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; 
}

【讨论】:

    【解决方案2】:

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

    做到这一点的唯一真正方法是在您的用户报告时找到每种情况,并手动处理它 - 即不要使用 operator&gt;&gt; 进行双重处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-27
      • 1970-01-01
      相关资源
      最近更新 更多