English 中文(简体)
当地独立执行
原标题:Locale-independent strtod implementation

I have a library which needs to parse double numbers which always use a point . as decimal separator. Unfortunately for this case, strtod() respects the locale which might use a different separator and thus parsing can fail. I can t setlocale() - it isn t thread-safe. So I m searching for a clean locale-independent strtod implementation now. I have found multiple implementations so far, but all of them look hacky or just like bad code. Can someone recommend a well-tested, working, clean (ANSI) C implementation for me?

最佳回答

Grab some known implementation (that doesn t depend on atof), such as the one distributed with ruby: ruby_1_8/missing/strtod.c.

问题回答

Warning: The proposed implementation from ruby contains bugs. I wouldn t mind the small difference pointed out by gavin, but if you try to parse something like "0.000000000000000000000000000000000000783475" you will get 0.0 instead of 7.834750e-37 (like the stock strtod() returns.)

其他解决办法:

#include <sstream>
#include "strtod_locale_independent.h"

extern "C" double strtod_locale_independent(const char* s)
{
     std::istringstream text( s );
     text.imbue(std::locale::classic());
     double result;
     text >> result;
     return result;
}

不过,我不知道这种情况是多么快。

继上述答复之后,我尝试在ruby_1_8/missing/strtod.c。 然而,对于一些投入,这在Mac和Atlin平台上,对水泥 par和从微粒中铺设提供了不同的答案:

char * endptr ;
double value1 = 1.15507e-173 ;
double value2 = strtod( "1.15507e-173", &endptr ) ;
double value3 = test_strtod( "1.15507e-173", &endptr ) ;
assert( sizeof( double ) == sizeof( unsigned long ) ) ;
printf( "value1 = %lg, 0x%lx.
", value1, *(unsigned long*)( &value1 ) ) ;
printf( "value2 = %lg, 0x%lx.
", value2, *(unsigned long*)( &value2 ) ) ;
printf( "value3 = %lg, 0x%lx.
", value2, *(unsigned long*)( &value3 ) ) ;
assert( value1 == value2 ) ;
assert( value1 == value3 ) ;

印刷

value1 = 1.15507e-173, 0x1c06dace8bda0ee0.
value2 = 1.15507e-173, 0x1c06dace8bda0ee0.
value3 = 1.15507e-173, 0x1c06dace8bda0edf.
Assertion failed: (value1 == value3), function main, file main.c, line 16.

因此,我的建议是在使用前检验所选择的执行。

也可在净校准、BSD风格许可证上查阅:

虽然时间已经很晚,但它可以为未来的读者提供一些见解。 按照上文Florian Kusche的答复模式,这里完全是“C”替代物,我已经成功地在各种护堤上进行了测试。 这一解决办法的一项要求是在实施<代码>strtod(之前暂时取消当地用户的系统。

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _LOCALE_N_ 13

int category[_LOCALE_N_] =
  {
   LC_ALL           , //   All of the locale
   LC_ADDRESS       , //   Formatting of addresses and geography-related items (*)
   LC_COLLATE       , //   String collation
   LC_CTYPE         , //   Character classification
   LC_IDENTIFICATION, //   Metadata describing the locale (*)
   LC_MEASUREMENT   , //   Settings related to measurements (metric versus US customary) (*)
   LC_MESSAGES      , //   Localizable natural-language messages
   LC_MONETARY      , //   Formatting of monetary values
   LC_NAME          , //   Formatting of salutations for persons (*)
   LC_NUMERIC       , //   Formatting of nonmonetary numeric values
   LC_PAPER         , //   Settings related to the standard paper size (*)
   LC_TELEPHONE     , //   Formats to be used with telephone services (*)
   LC_TIME            //   Formatting of date and time values
  };

void _store_locale_info(char *vals[_LOCALE_N_])
{
  /* store the current locale information in an array of strings for future use */

  int i;

  for (i=0; i<_LOCALE_N_; i++)
  {
    char *loc_str = setlocale(category[i], "");
    int L = strlen(loc_str);
    vals[i] = calloc(L+1, sizeof(char));
    strncpy(vals[i], setlocale(category[i], ""), L+1);
  }
}

void _restore_locale_info(char *vals[_LOCALE_N_])
{
  /* restore the locale information from a previosly-populated array of strings */

  int i;

  for (i=0; i<_LOCALE_N_; i++)
  {
    if (vals[i])
    {
      setlocale(category[i], vals[i]);
      free(vals[i]);
    }
  }
}

double _strtod_c (const char *string, char **endPtr)
{
  /* Wrapper function for strtod() that enforces the "C" locale before converting a floating-point
   * number from an ASCII decimal representation to internal double-precision format. */

  char *vals[_LOCALE_N_];
  double rval = 0;
  _store_locale_info(vals);
  rval = strtod(string, endPtr);
  _restore_locale_info(vals);
  return rval;
}

int main()
{
  char *str = "1024.123456";
  char **endPtr;
  char locale_str[100];

  printf("
str = "%s"

", str);
  printf("Locale
", str);

  strcpy(locale_str, setlocale(LC_ALL, "C"));
  printf("%-6s :: strtod(str, endPtr) = %.15g
", locale_str, strtod(str, endPtr));

  strcpy(locale_str, setlocale(LC_ALL, "de_DE"));
  printf("%-6s :: strtod(str, endPtr) = %.15g
", locale_str, strtod(str, endPtr));

  printf("---
");

  strcpy(locale_str, setlocale(LC_ALL, "C"));
  printf("%-6s :: _strtod_c(str, endPtr) = %.15g
", locale_str, _strtod_c(str, endPtr));

  strcpy(locale_str, setlocale(LC_ALL, "de_DE"));
  printf("%-6s :: _strtod_c(str, endPtr) = %.15g
", locale_str, _strtod_c(str, endPtr));

  printf("
");
}

相容的含水层安装的预期产出是


str = "1024.123456"

Locale
C      :: strtod(str, endPtr) = 1024.123456
de_DE  :: strtod(str, endPtr) = 1024
---
C      :: _strtod_c(str, endPtr) = 1024.123456
de_DE  :: _strtod_c(str, endPtr) = 1024.123456

既然你不能改变当地情况,因为申请是多方面的,而且如果你希望得到确切的行为,重新实施<代码>strtod<>/code”是一项令人生畏的任务,因此,这里是一个简单的替代方案,在大多数情况下应当发挥作用:

#include <locale.h>
#include <stdlib.h>
#include <string.h>

double my_strtod(const char *s, char **endp) {
    char buf[1024];
    char *p = strchr(s,  . );
    if (p == NULL || (size_t)(p - s) >= sizeof(buf)) {
        return strtod(s, endp);
    }
    struct lconv *lp = localeconv();
    *buf =   ;
    strncat(buf, s, sizeof(buf) - 1);
    buf[p - s] = *lp->decimal_point;
    double v = strtod(buf, &p);
    if (endp) {
        *endp = s + (p - buf);
    }
    return v;
}

如果在很长的路段上使用my_strtod(),那么分析说明理由的初步部分以决定从中复制多少个性,可能更为有效。

如果您使用my_strtod(>>,仅用于目前校对的可变阵列,则你只能暂时取代第一个期间<代码>。

#include <locale.h>
#include <stdlib.h>
#include <string.h>

double my_strtod(char *s, char **endp) {
    char *p = strchr(s,  . );
    if (p != NULL) {
        struct lconv *lp = localeconv();
        *p = *lp->decimal_point;
    }
    double v = strtod(buf, endp);
    if (p != NULL) {
        *p =  . ;
    }
    return v;
}

当然,这一方法使用<代码>strtod(),因此,它假定,其他同时使用的镜子与目前的地方并不相差。





相关问题
Simple JAVA: Password Verifier problem

I have a simple problem that says: A password for xyz corporation is supposed to be 6 characters long and made up of a combination of letters and digits. Write a program fragment to read in a string ...

Case insensitive comparison of strings in shell script

The == operator is used to compare two strings in shell script. However, I want to compare two strings ignoring case, how can it be done? Is there any standard command for this?

Trying to split by two delimiters and it doesn t work - C

I wrote below code to readin line by line from stdin ex. city=Boston;city=New York;city=Chicago and then split each line by ; delimiter and print each record. Then in yet another loop I try to ...

String initialization with pair of iterators

I m trying to initialize string with iterators and something like this works: ifstream fin("tmp.txt"); istream_iterator<char> in_i(fin), eos; //here eos is 1 over the end string s(in_i, ...

break a string in parts

I have a string "pc1|pc2|pc3|" I want to get each word on different line like: pc1 pc2 pc3 I need to do this in C#... any suggestions??

Quick padding of a string in Delphi

I was trying to speed up a certain routine in an application, and my profiler, AQTime, identified one method in particular as a bottleneck. The method has been with us for years, and is part of a "...

热门标签