假设我必须读取一个包含一堆浮点数的文件。数字可以是1e+10
,5
,-0.15
等,即任何通用浮点数,使用小数点(这是固定的!) 。但是,我的代码是另一个应用程序的插件,我无法控制当前的区域设置。例如,它可能是俄语,并且LC_NUMERIC规则要求使用小数逗号。因此,Pi预计拼写为“3,1415 ......”和
sscanf("3.14", "%f", &x);
返回“1”,x包含“3.0”,因为它拒绝解析超过'。'在字符串中。
我需要忽略这些数字解析任务的语言环境。
如何做到这一点?
我可以编写一个parseFloat函数,但这似乎是浪费 我还可以保存当前的语言环境,将其暂时重置为“C”,读取文件,然后恢复到保存的文件。这有什么性能影响?在一些OS / libc组合中,setlocale()可能会非常慢,它在引擎盖下真正做了什么? 另一种方式是使用iostreams,但他们的表现并不是很好。
所以我很困惑。在这种情况下你们做了什么?
干杯!
答案 0 :(得分:5)
我个人的偏好是永远不要使用LC_NUMERIC
,即只使用其他类别调用setlocale
,或者在使用setlocale
致电LC_ALL
后,使用setlocale(LC_NUMERIC, "C");
。否则,如果您想使用标准库以标准形式打印或解析数字以进行交换,那么您就完全没有运气了。
如果您很幸运能够使用符合POSIX 2008标准的系统,则可以使用uselocale
和*_l
系列函数来改善情况。至少有两种基本方法:
保持默认语言环境未设置(至少包括LC_NUMERIC
; LC_CTYPE
这样的麻烦部分应该始终设置),并传递{{1}只有当您希望以符合其自身文化期望的方式向用户呈现内容时,才能将用户区域设置的对象发送到相应的locale_t
函数;否则使用默认的C语言环境。
让您的代码需要使用数据进行交换,保留C语言环境的*_l
对象,并在需要处理数据时使用locale_t
来回切换以交换的标准形式,或使用适当的uselocale
函数(但没有*_l
)。
请注意,实现自己的浮点解析器不容易,除非您是数值计算方面的专家,否则它可能不是解决问题的正确方法。正确的做法非常困难。
答案 1 :(得分:3)
我不确定如何在C中解决它。
但是C ++流(可以)有一个唯一的区域设置对象。
std::stringstream dataStream;
dataStream.imbue(std::locale("C"));
// Note: You must imbue the stream before you do anything wit it.
// If any operations have been performed then an imbue() can
// be silently ignored by the stream (which is a pain to debug).
dataStream << "3.14";
float x;
dataStream >> x;
答案 2 :(得分:3)
这是我过去对这些东西做过的事情。
目标是使用具有C语言环境数字表示的区域设置相关数字转换器。当然,理想的做法是使用非依赖于语言环境的转换器,或者不改变语言环境等等,但有时你只需要忍受你所拥有的东西。区域设置支持在几个方面严重破坏,这就是其中之一。&lt; / rant&gt;
首先,使用类似C
语法的简单模式(数字预处理标记)将数字提取为字符串。为了与scanf一起使用,我做了一个更简单的方法:
" %1[-+0-9.]%[-+0-9A-Za-z.]"
这可以进一步简化,具体取决于您在输入流中可能会遇到的其他内容。你唯一需要做的就是不要超过数字的末尾;只要你不允许数字立即被字母跟随,而不干预空格,上面的方法就可以了。
现在,使用struct lconv
获取代表当前区域设置的man 7 locale
(localeconv(3)
)。该结构中的第一个条目是const char* decimal_point
;用该值替换字符串中的所有'.'
个字符。 (您可能还需要替换'+'
和'-'
字符,但大多数区域设置都不会更改它们,并且lconv
结构中的符号字段会记录为仅适用于货币转换。)最后,通过strtod
提供生成的字符串,看看它是否通过。
这不是一个完美的算法,特别是因为它并不总是很容易知道给定库实际上是如何符合语言环境的,所以你可能想要做一些autoconf的东西来为你的库配置它#39 ;实际上正在编译。