8

假设我必须读取一个文件,其中包含一堆浮点数。数字可以是,等1e+10,即任何通用浮点数,使用小数点(这是固定的!)。但是,我的代码是另一个应用程序的插件,我无法控制当前的语言环境。例如,它可能是俄语,并且那里的 LC_NUMERIC 规则要求使用十进制逗号。因此,Pi 应该被拼写为“3,1415...”,并且5-0.15

sscanf("3.14", "%f", &x); 

返回“1”,并且 x 包含“3.0”,因为它拒绝解析 '.' 在字符串中。

我需要忽略此类数字解析任务的语言环境。

如何做到这一点?

我可以编写一个 parseFloat 函数,但这似乎是一种浪费。
我还可以保存当前语言环境,将其临时重置为“C”,读取文件,然后恢复到保存的位置。这对性能有何影响?setlocale() 在某些 OS/libc 组合上可能会非常慢,它到底在做什么?
另一种方法是使用 iostreams,但同样它们的性能并不出色。

所以我很困惑。大家遇到这种情况怎么办?

干杯!

4

3 回答 3

7

我个人的偏好是从不使用LC_NUMERIC,即只调用setlocale其他类别,或者在调用之后setlocale使用。否则,如果您想使用标准库以标准形式打印或解析数字以进行交换,那么您将完全不走运。LC_ALLsetlocale(LC_NUMERIC, "C");

如果您有幸使用了符合 POSIX 2008 的系统,则可以使用uselocaleand*_l系列函数来使情况稍微好一些。至少有两种基本方法:

  1. 保留未设置的默认区域设置(至少应该始终LC_NUMERIC设置像;这样的麻烦部分),并且仅当您想以符合他们自己文化的方式向用户呈现事物时,才将用户区域设置的对象传递给适当的函数期望; 否则使用默认的 C 语言环境。LC_CTYPElocale_t*_l

  2. 让需要处理数据以进行交换的代码围绕 C 语言环境的对象保留,并在需要以标准形式处理数据以进行交换时locale_t来回切换,或者使用适当的函数(但有没有)。uselocale*_lscanf_l

请注意,实现自己的浮点解析器并不容易,除非您是数值计算方面的专家,否则可能不是解决问题的正确方法。把它做好是非常困难的。

于 2012-12-17T18:32:08.227 回答
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;
于 2012-12-17T19:16:51.437 回答
3

这是我过去对这些东西所做的事情。

目标是使用具有 C 语言环境数字表示的依赖于语言环境的数字转换器。当然,理想的情况是使用不依赖于语言环境的转换器,或者不更改语言环境等,但有时你只需要忍受你所拥有的。语言环境支持在几个方面受到严重破坏,这就是其中之一。</rant>

首先,使用类似于C用于数字预处理标记的语法的简单模式将数字提取为字符串。为了与scanf一起使用,我做了一个更简单的:

" %1[-+0-9.]%[-+0-9A-Za-z.]"

这可以进一步简化,具体取决于您对输入流的期望。您唯一需要做的就是不要阅读超出数字末尾的内容;只要您不允许数字后面紧跟字母,而没有插入空格,上述内容就可以正常工作。

Now, get the struct lconv (man 7 locale) representing the current locale using localeconv(3). The first entry in that struct is const char* decimal_point; replace all of the '.' characters in your string with that value. (You might also need to replace '+' and '-' characters, although most locales don't change them, and the sign fields in the lconv struct are documented as only applying to currency conversions.) Finally, feed the resulting string through strtod and see if it passes.

This is not a perfect algorithm, particularly since it's not always easy to know how locale-compliant a given library actually is, so you might want to do some autoconf stuff to configure it for the library you're actually compiling with.

于 2012-12-17T20:11:59.063 回答