sscanf()和locales.如何真正解析"3.14"之类的东西?

anr*_*eff 8 c c++ parsing scanf

假设我必须读取一个包含一堆浮点数的文件.这些数字可以像1e+10,5,-0.15等等,即任何通用的浮点数字,用小数点(这是固定的!).但是,我的代码是另一个应用程序的插件,我无法控制当前的区域设置.例如,它可能是俄语,并且LC_NUMERIC规则要求使用小数逗号.因此,Pi预计拼写为"3,1415 ......",并且

sscanf("3.14", "%f", &x); 
Run Code Online (Sandbox Code Playgroud)

返回"1",x包含"3.0",因为它拒绝解析过去的'.' 在字符串中.

我需要忽略这些数字解析任务的语言环境.

怎么做到这一点?

我可以写一个parseFloat函数,但这似乎是浪费.
我还可以保存当前的语言环境,将其暂时重置为"C",读取文件,然后恢复到保存的文件.这有什么性能影响?在某些操作系统/ libc组合中,setlocale()可能会非常慢,它在幕后真的做了什么?
另一种方式是使用iostreams,但他们的表现并不是很好.

所以我很困惑.在这种情况下你们做了什么?

干杯!

R..*_*R.. 7

我个人的偏好是永远不会使用LC_NUMERIC,即只需调用setlocale与其他类别,或打完电话后setlocaleLC_ALL,使用setlocale(LC_NUMERIC, "C");.否则,如果您想使用标准库以标准形式打印或解析数字以进行交换,那么您就完全没有运气了.

如果你有幸成为与POSIX符合2008系统上,你可以使用uselocale*_l家庭的功能,使情况有所好转.至少有两种基本方法:

  1. 保持默认语言环境未设置(至少是麻烦的部分,例如LC_NUMERIC; LC_CTYPE应该总是设置),并且只有当你想以符合他们自己文化的方式向用户呈现内容时,才locale_t将用户区域设置的对象传递给适当的*_l函数.期望; 否则使用默认的C语言环境.

  2. 让您的代码需要处理交换数据,保留locale_tC语言环境的对象,并uselocale在需要使用标准格式的数据进行交换时来回切换,或使用适当的*_l函数(但有不scanf_l).

请注意,实现自己的浮点解析器并不容易,除非您是数值计算方面的专家,否则可能无法解决问题.做对了很难.

  • 使用语言环境功能可在线程系统中使用; 使用`setlocale()`可能会破坏并行工作的其他线程. (2认同)

ric*_*ici 3

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

目标是使用具有 C 语言环境数字表示形式的语言环境相关数字转换器。当然,理想的情况是使用不依赖于语言环境的转换器,或者不更改语言环境等,但有时您只需要接受现有的东西即可。区域设置支持在多个方面受到严重破坏,这就是其中之一。</rant>

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

" %1[-+0-9.]%[-+0-9A-Za-z.]"
Run Code Online (Sandbox Code Playgroud)

这可以进一步简化,具体取决于您在输入流中可能期望的其他内容。您唯一需要做的就是不要读取超出数字末尾的内容;只要您不允许数字后面紧跟着字母,且不插入空格,上面的代码就可以正常工作。

现在,使用 获取表示当前区域设置的struct lconv( ) 。该结构中的第一个条目是;将字符串中的所有字符替换为该值。(您可能还需要替换和字符,尽管大多数区域设置不会更改它们,并且结构中的符号字段被记录为仅适用于货币转换。)最后,输入生成的字符串并查看它是否通过。man 7 localelocaleconv(3)const char* decimal_point'.''+''-'lconvstrtod

这不是一个完美的算法,特别是因为要知道给定库实际上与区域设置的兼容程度并不总是那么容易,因此您可能需要执行一些 autoconf 操作来为您实际编译的库配置它。