std::ostringstream & LC_NUMERIC

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • mathieu

    std::ostringstream & LC_NUMERIC

    Hi,

    I am playing with the following C++ piece of code (*). At least on
    my system debian/gcc 4.3 it looks like I am not writing out a floating
    point separator as a comma. what are the operation affected by the
    LC_NUMERIC env var value ?

    Thanks
    -Mathieu


    (*)
    #include <sstream>
    #include <iostream>
    #include <stdlib.h>

    int main(int argc, char *argv[])
    {
    setenv("LC_NUME RIC", "fr_FR", 1);
    std::ostringstr eam os;
    double d = 1.2;
    os << d;
    std::string s = os.str();
    std::cout << s << std::endl;
    std::string::si ze_type pos_comma = s.find( "," );
    if( pos_comma != std::string::np os )
    {
    return 1;
    }
    std::string::si ze_type pos_dot = s.find( "." );
    if( pos_dot == std::string::np os )
    {
    return 1;
    }
    std::cout << "dot found" << std::endl;
    return 0;
    }
  • James Kanze

    #2
    Re: std::ostringstr eam &amp; LC_NUMERIC

    On Oct 7, 2:49 pm, mathieu <mathieu.malate ...@gmail.comwr ote:
    I am playing with the following C++ piece of code (*). At
    least on my system debian/gcc 4.3 it looks like I am not
    writing out a floating point separator as a comma. what are
    the operation affected by the LC_NUMERIC env var value ?
    In C++, nothing. All setting an envirionment variable does is
    make it available to your process and its sub-processes (at
    least under Unix---what it does outside of your process is
    really very system dependent).

    What you usually do in C++ is just the opposite; you read the
    variable to know how to set your locale. While creating a
    locale with the name "" (an empty string) is formally
    implementation defined, it is a more or less established
    convention, at least under Unix, that this locale will depend on
    all of the LC_ environment variables. In other words, you will
    use the locale the user specified.
    #include <sstream>
    #include <iostream>
    #include <stdlib.h>
    int main(int argc, char *argv[])
    {
    setenv("LC_NUME RIC", "fr_FR", 1);
    This could affect creating a locale with the empty string, but
    the main reason you would want to do this would be to inform
    sub-processes to use the "fr_FR" locale.
    std::ostringstr eam os;
    double d = 1.2;
    os << d;
    std::string s = os.str();
    std::cout << s << std::endl;
    std::string::si ze_type pos_comma = s.find( "," );
    if( pos_comma != std::string::np os )
    {
    return 1;
    }
    std::string::si ze_type pos_dot = s.find( "." );
    if( pos_dot == std::string::np os )
    {
    return 1;
    }
    std::cout << "dot found" << std::endl;
    return 0;
    }
    And you've never touched the actual locales used by your
    program. In general:

    -- A locale (i.e. std::locale) object created with the empty
    string as a name will normally correspond to the "locale"
    active in whatever process invoked you---under Unix, it will
    normally use the LC_ environment variables, and perhaps
    LANG, to determine this.

    -- There is a global locale, used whenever you do not specify a
    locale. On program start up, this is set to the "C" locale
    (which might be appropriate for parsing C++ sources, but not
    for much else); you can change it by calling
    std::locale::gl obal with the locale you want as the global
    locale.

    Combined with the previous point: almost every program which
    generates human output should start with:
    std::locale::gl obal( std::locale( "" ) ) ;
    Or if you really want to force a French locale, even for
    users who don't want it:
    std::locale::gl obal( std::locale( "fr_FR" ) ) ;
    (assuming the Unix naming conventions for locales.)

    -- For any given IO, you can force the locale just for that
    stream, by using imbue. Thus, if you've done the above (and
    thus don't really know what the global locale is), but want
    to output C++ code (for example), you should imbue the file
    with the "C" locale; either:
    output.imbue( std::locale( "C" ) ) ;
    or:
    output.imbue( std::locale::cl assic() ) ;
    (which uses a special static member function to get this
    very special locale). Similarly, if you want to force
    output in the French locale for just this file:
    output.imbue( std::locale( "fr_FR" ) ) ;

    -- Finally, you can mix locales. If you want the classic
    locale in general, but you want numbers formatted according
    to the rules in French, you can create a custom locale for
    this, e.g.
    std::locale( std::locale::cl assic(),
    "fr_FR",
    std::locale::nu meric )
    In otherwords, copy std::locale::cl assic() (the first
    argument), except for the numeric category (the third
    argument, which can be an or'ed list of facets), which is
    taken from the locale named "fr_FR".

    In general, this is fairly tricky, however, and you have to
    know what you are doing, and how the different "facets"
    interact. (Each category is implemented by one or more
    facets.)

    --
    James Kanze (GABI Software) email:james.kan ze@gmail.com
    Conseils en informatique orientée objet/
    Beratung in objektorientier ter Datenverarbeitu ng
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

    Comment

    • mathieu

      #3
      Re: std::ostringstr eam &amp; LC_NUMERIC

      On Oct 8, 11:00 am, James Kanze <james.ka...@gm ail.comwrote:
      On Oct 7, 2:49 pm, mathieu <mathieu.malate ...@gmail.comwr ote:
      >
      I am playing with the following C++ piece of code (*). At
      least on my system debian/gcc 4.3 it looks like I am not
      writing out a floating point separator as a comma. what are
      the operation affected by the LC_NUMERIC env var value ?
      >
      In C++, nothing. All setting an envirionment variable does is
      make it available to your process and its sub-processes (at
      least under Unix---what it does outside of your process is
      really very system dependent).
      >
      What you usually do in C++ is just the opposite; you read the
      variable to know how to set your locale. While creating a
      locale with the name "" (an empty string) is formally
      implementation defined, it is a more or less established
      convention, at least under Unix, that this locale will depend on
      all of the LC_ environment variables. In other words, you will
      use the locale the user specified.
      >
      #include <sstream>
      #include <iostream>
      #include <stdlib.h>
      int main(int argc, char *argv[])
      {
      setenv("LC_NUME RIC", "fr_FR", 1);
      >
      This could affect creating a locale with the empty string, but
      the main reason you would want to do this would be to inform
      sub-processes to use the "fr_FR" locale.
      >
      >
      >
      std::ostringstr eam os;
      double d = 1.2;
      os << d;
      std::string s = os.str();
      std::cout << s << std::endl;
      std::string::si ze_type pos_comma = s.find( "," );
      if( pos_comma != std::string::np os )
      {
      return 1;
      }
      std::string::si ze_type pos_dot = s.find( "." );
      if( pos_dot == std::string::np os )
      {
      return 1;
      }
      std::cout << "dot found" << std::endl;
      return 0;
      }
      >
      And you've never touched the actual locales used by your
      program. In general:
      >
      -- A locale (i.e. std::locale) object created with the empty
      string as a name will normally correspond to the "locale"
      active in whatever process invoked you---under Unix, it will
      normally use the LC_ environment variables, and perhaps
      LANG, to determine this.
      >
      -- There is a global locale, used whenever you do not specify a
      locale. On program start up, this is set to the "C" locale
      (which might be appropriate for parsing C++ sources, but not
      for much else); you can change it by calling
      std::locale::gl obal with the locale you want as the global
      locale.
      >
      Combined with the previous point: almost every program which
      generates human output should start with:
      std::locale::gl obal( std::locale( "" ) ) ;
      Or if you really want to force a French locale, even for
      users who don't want it:
      std::locale::gl obal( std::locale( "fr_FR" ) ) ;
      (assuming the Unix naming conventions for locales.)
      >
      -- For any given IO, you can force the locale just for that
      stream, by using imbue. Thus, if you've done the above (and
      thus don't really know what the global locale is), but want
      to output C++ code (for example), you should imbue the file
      with the "C" locale; either:
      output.imbue( std::locale( "C" ) ) ;
      or:
      output.imbue( std::locale::cl assic() ) ;
      (which uses a special static member function to get this
      very special locale). Similarly, if you want to force
      output in the French locale for just this file:
      output.imbue( std::locale( "fr_FR" ) ) ;
      >
      -- Finally, you can mix locales. If you want the classic
      locale in general, but you want numbers formatted according
      to the rules in French, you can create a custom locale for
      this, e.g.
      std::locale( std::locale::cl assic(),
      "fr_FR",
      std::locale::nu meric )
      In otherwords, copy std::locale::cl assic() (the first
      argument), except for the numeric category (the third
      argument, which can be an or'ed list of facets), which is
      taken from the locale named "fr_FR".
      >
      In general, this is fairly tricky, however, and you have to
      know what you are doing, and how the different "facets"
      interact. (Each category is implemented by one or more
      facets.)
      Thanks ! That's extremely detailed :)

      Since I am a just library author I can not rely on the fact that my
      user will start their main program with
      std::locale::gl obal( std::locale( "" ) ) ;

      Instead I'll have to make sure any o*stream are created (within the
      lib, or externally by the user) with
      os.imbue(std::l ocale::classic( ));

      Regards
      -Mathieu

      Comment

      Working...