我如何在 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>>
作为双倍。
我正在维护一个大型开源项目,所以我在 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>>
作为双倍。