为C printf设置千位分隔符

时间:2015-02-26 14:11:23

标签: c printf locale

我有这个C代码:

locale_t myLocale = newlocale(LC_NUMERIC_MASK, "en_US", (locale_t) 0);
uselocale(myLocale);
ptrLocale = localeconv();
ptrLocale->thousands_sep = (char *) "'";

int i1 = snprintf( s1, sizeof(s1), "%'d", 123456789);

s1中的输出为123,456,789

即使我将->thousands_sep设置为',也会被忽略。有没有办法将任何字符设置为千位分隔符?

5 个答案:

答案 0 :(得分:3)

功能localeconv()只需阅读定位设置,ptrLocale->thousands_sep本身不会更改当前区域设置的设置。

修改

我不知道如何在C中执行此操作,但可以找到许多带有C ++输出的示例。 请参阅C ++中的以下示例:

#include <iostream>
#include <locale>
using namespace std;

struct myseps : numpunct<char> { 
   // use ' as separator
   char do_thousands_sep() const { return '\''; } 

   // digits are grouped by 3
   string do_grouping() const { return "\3"; }
};

int main() {
  cout.imbue(locale(locale(), new myseps));
  cout << 1234567; // the result will be 1'234'567
}

编辑2:

C ++参考文献说:

  

localeconv()返回一个指向struct lconv类型的填充对象的指针。对象中包含的值可以通过后续调用localeconv来覆盖,而不是直接修改对象。使用LC_ALL,LC_MONETARY或LC_NUMERIC的类别值调用setlocale会覆盖结构的内容。

我在MS Visual Studio 2012中尝试了以下示例(我知道这是不好的和不安全的样式):

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

int main() {
    setlocale(LC_NUMERIC, "");
    struct lconv *ptrLocale = localeconv();
    strcpy(ptrLocale->decimal_point, ":");
    strcpy(ptrLocale->thousands_sep, "'");
    char str[20];
    printf("%10.3lf \n", 13000.26);
    return 0;
}

我看到了结果:

  13000:260

因此,可以假设通过decimal_point收到的指针可以更改thousands_seplocaleconv(),但printf会忽略thousands_sep。< / p>

编辑3:

更新了C ++示例:

#include <iostream>
#include <locale>
#include <sstream>
using namespace std;

struct myseps : numpunct<char> { 
   // use ' as separator
   char do_thousands_sep() const { return '\''; } 

   // digits are grouped by 3
   string do_grouping() const { return "\3"; }
};

int main() {
  stringstream ss;
  ss.imbue(locale(locale(), new myseps));
  ss << 1234567;  // printing to string stream with formating
  printf("%s\n", ss.str().c_str()); // just output when ss.str() provide string, and c_str() converts it to char*
}

答案 1 :(得分:3)

如何更改printf()的千位分隔符:

,真的非常糟糕
  1. 下载GNU libc。
  2. 运行configure --prefix=/usr/glibc-version命令
  3. 运行make -j 8
  4. 使用make输出
  5. 中的所有开关获取非常长的编译器命令
  6. 编写C源文件setMyThousandSeparator.c - 内容见下文
  7. 使用第3点的gcc开关编译此源文件。
  8. setMyThousandSeparator("'")来电之前,您正常的C源代码调用printf()函数中的
  9. setMyThousandSeparator.o与您的项目联系起来。
  10. 目前我在链接libc静态时尝试了它,但它确实有效。

    setMyThousandSeparator.c的内容:

    #include <locale/localeinfo.h>
    
    void setMyThousandSeparator(char * sMySeparator)
    {
        _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP) = sMySeparator;
    }
    

    <强>的信息: 此解决方案是线程安全的,因为它正在访问与printf()相同的数据!

答案 2 :(得分:1)

这是一个非常简单的解决方案,适用于每个Linux发行版,并且不需要 - 作为我的第一个答案 - glibc hack:

所有这些步骤必须在构建目录中的 origin glibc目录 - NOT 中执行 - 在使用glibc版本构建后这个instructions建议的单独构建目录。

我的新locale文件名为en_AT

  1. 在现有文件localedata/locales/的{​​{1}}目录中创建新文件en_US
  2. en_AT的所有条目更改为thousands_sep或您希望拥有的任何字符作为千位分隔符。
  3. 将新文件内的所有thousands_sep "<U0027>"更改为en_US
  4. 将行en_AT添加到文件localedata/SUPPORTED
  5. 构建目录en_AT.UTF-8/UTF-8 \
  6. 中运行
  7. 新的make localedata/install-locales会自动添加到系统中,并且 即时 可供该计划使用。
  8. 在C / C ++程序中,您可以切换到新的千位分隔符:

      

    locale

    使用setlocale( LC_ALL, "en_AT.UTF-8" );生成此输出

      

    1&#39; 000&#39; 000

    备注:当您在程序中需要在运行时确定的不同本地化时,您可以在printf( "%'d", 1000000 );页面中使用此example来加载请求的man 1}}只需替换locale中的LC_NUMERIC设置。

答案 3 :(得分:0)

此答案来自VolAnd's one

根据this source,千分隔符仅与非标准'标志一起使用。

因此,如果您的printf与POSIX.1-2008兼容,则可以使用:

setlocale(LC_NUMERIC, "");
struct lconv *ptrLocale = localeconv();
ptrLocale->decimal_point = ":";
ptrLocale->thousands_sep = "'";
char str[20];
printf("%'10.3lf \n", 13000.26);
return 0;

答案 4 :(得分:0)

这是我用于uint64_t类型的专用C函数,但是可以很容易地将其泛化。基本上,它会将一千个分隔符插入到snprintf()生成的字符串中。

此方法与LOCALE,使用的C标准等无关-当然,您不必重新编译GNU libc;)

#if __WORDSIZE == 64
   #define PRT_U64 "lu"
#else
   #define PRT_U64 "llu"
#endif

char* th_sep_u64(uint64_t val, char* buf) {
   char tmpbuf[32]; //18'446'744'073'709'551'615 -> 26 chars
   int  nch, toffs, pos;
   pos   = 1;
   toffs = 31;
   nch   = snprintf(tmpbuf, 32, "%"PRT_U64, val);
   nch  -- ;
   buf[toffs] = 0;

   for (; nch>=0; --nch) {
      toffs -- ;
      buf[toffs] = tmpbuf[nch];
      if ((0 == (pos % 3)) && (nch > 0)) {
         toffs -- ;
         buf[toffs] = '\''; //inject the separator
      }
      pos ++ ;
   }
   buf += toffs;
   return buf;
}

用法:

{
   char     cbuf[32]; 
   uint64_t val = 0xFFFFFFFFFFFFFFFFll;

   printf("%s", th_sep_u64(val, cbuf));

   //result: 18'446'744'073'709'551'615
}

致谢