【问题标题】:How to format a number with thousands separator in C/C++如何在 C/C++ 中格式化带有千位分隔符的数字
【发布时间】:2017-09-14 21:38:17
【问题描述】:

我正在尝试完成这个简单的任务。只是使用 C 或 C++ 格式化数字,但在 Windows CE 编程下。

在这种环境中,inbue 和 setlocale 方法都不起作用。

最后我没有成功:

char szValue[10];
sprintf(szValue, "%'8d", iValue);

有什么想法吗?

【问题讨论】:

  • 如果需要一个固定的格式,那么你自己做可能是你最好的选择。如果您需要对语言环境敏感的演示文稿,那么我怀疑GetNumberFormat 是要走的路。
  • ,3C 和 C++ 是不同的语言。问这两个问题就像在一个帖子中问两个问题,这违反了网站规则。当您使用 C 函数 printf 时,C 似乎是您的重点。说:你发现了什么?您应该知道我们不是编码服务。你自己尝试了什么?显示您的代码并说明什么不起作用。不得不说,这已经够可悲了。
  • 当然 C 和 C++ 是不同的语言。如果我发布这个关于 C 或 C++ 的问题仅意味着我将接受 C 或 C++ 的解决方案。没关系,因为 C++ 编译器也编译 C 代码。知道了?或者你想让我用同一个主体发布2个不同的问题,但用C++改变C?这不是浪费时间吗? C++ 程序员也知道如何用纯 C 编程(通常)。但没关系..我开发了一个使用 std::string 来完成这项任务的函数,它就像一个魅力。
  • 关于 C 和 C++ 标签:一种语言的好答案可能无法在另一种语言中编译。示例:我的 answer 无法在 C++ 中编译。自 C99 以来,C 已经偏离了 C++ 可以编译的内容。使用双重标记,该帖子似乎需要一个同时适用于这两种语言的解决方案,即使对于单独的语言不是最佳的。正是这种缺乏明确的目标,才用双标签攻击 DV。
  • 既然您似乎可以使用 C++,您可以像这样使用'123'456'789。它从 C++14 开始工作,可以放在数字之间的任何位置,甚至像 1'2'3'4

标签: c windows-mobile windows-ce pocketpc


【解决方案1】:

我用这个:

string thousands_separator(long long k, string symbol=",") {
    int l, c, i;
    string fin, s, u, rev;
    bool min = false;
    fin = "";
    c = 0;
    if(k < -999){
        k *= -1;
        min = true;
    }
    s = to_string(k);
    if(k > 999){
        l = s.length() - 1;
        for (i = l; i >= 0; i--) {
            fin += s[i];
            c++;
            if(c%3 == 0){
                fin += symbol;
            }
        }
        rev = fin;
        fin = "";
        l = rev.length() - 1;
        for (i = l; i >= 0; i--) {
            fin += rev[i];
        }
        u = fin[0];
        if(u == symbol){
            fin.erase(fin.begin());
        }
        if(min){
            fin.insert(0, "-");
        }
        return fin;
    } else {
        return s;
    }
}

【讨论】:

    【解决方案2】:

    这些函数在 C++ 中工作,用于字符串中的数字,带或不带小数。

    下一个函数不支持负字符串数字或小数分隔符,但很简单:

    std::string quickAddThousandSeparators(std::string value, char thousandSep = ',')
    {
        int len = value.length();
        int dlen = 3;
    
        while (len > dlen) {
            value.insert(len - dlen, 1, thousandSep);
            dlen += 4;
            len += 1;
        }
        return value;
    }
    

    下一个函数支持负字符串数字和小数分隔符:

    std::string addThousandSeparators(std::string value, char thousandSep = ',', char decimalSep = '.', char sourceDecimalSep = '.')
    {
        int len = value.length();
        int negative = ((len && value[0] == '-') ? 1: 0);
        int dpos = value.find_last_of(sourceDecimalSep);
        int dlen = 3 + (dpos == std::string::npos ? 0 : (len - dpos));
    
        if (dpos != std::string::npos && decimalSep != sourceDecimalSep) {
            value[dpos] = decimalSep;
        }
    
        while ((len - negative) > dlen) {
            value.insert(len - dlen, 1, thousandSep);
            dlen += 4;
            len += 1;
        }
        return value;
    }
    

    并且 gtest 通过了测试:

    TEST (quickAddThousandSeparators, basicNumbers) {
        EXPECT_EQ("", quickAddThousandSeparators(""));
        EXPECT_EQ("1", quickAddThousandSeparators("1"));
        EXPECT_EQ("100", quickAddThousandSeparators("100"));
        EXPECT_EQ("1,000", quickAddThousandSeparators("1000"));
        EXPECT_EQ("10,000", quickAddThousandSeparators("10000"));
        EXPECT_EQ("100,000", quickAddThousandSeparators("100000"));
        EXPECT_EQ("1,000,000", quickAddThousandSeparators("1000000"));
        EXPECT_EQ("1,000,000,000", quickAddThousandSeparators("1000000000"));
        EXPECT_EQ("1,012,789,345,456,123,678,456,345,809", quickAddThousandSeparators("1012789345456123678456345809"));
    }
    
    TEST (quickAddThousandSeparators, changeThousandSeparator) {
        EXPECT_EQ("", quickAddThousandSeparators("",'.'));
        EXPECT_EQ("1", quickAddThousandSeparators("1",'.'));
        EXPECT_EQ("100", quickAddThousandSeparators("100", '.'));
        EXPECT_EQ("1.000", quickAddThousandSeparators("1000", '.'));
        EXPECT_EQ("1.000.000.000", quickAddThousandSeparators("1000000000", '.'));
        EXPECT_EQ("1.012.789.345.456.123.678.456.345.809", quickAddThousandSeparators("1012789345456123678456345809", '.'));
    }
    
    TEST (addThousandSeparators, basicNumbers) {
        EXPECT_EQ("", addThousandSeparators(""));
        EXPECT_EQ("1", addThousandSeparators("1"));
        EXPECT_EQ("100", addThousandSeparators("100"));
        EXPECT_EQ("1,000", addThousandSeparators("1000"));
        EXPECT_EQ("10,000", addThousandSeparators("10000"));
        EXPECT_EQ("100,000", addThousandSeparators("100000"));
        EXPECT_EQ("1,000,000", addThousandSeparators("1000000"));
        EXPECT_EQ("1,000,000,000", addThousandSeparators("1000000000"));
        EXPECT_EQ("1,012,789,345,456,123,678,456,345,809", addThousandSeparators("1012789345456123678456345809"));
    }
    
    TEST (addThousandSeparators, negativeBasicNumbers) {
        EXPECT_EQ("", addThousandSeparators(""));
        EXPECT_EQ("-1", addThousandSeparators("-1"));
        EXPECT_EQ("-100", addThousandSeparators("-100"));
        EXPECT_EQ("-1,000", addThousandSeparators("-1000"));
        EXPECT_EQ("-10,000", addThousandSeparators("-10000"));
        EXPECT_EQ("-100,000", addThousandSeparators("-100000"));
        EXPECT_EQ("-1,000,000", addThousandSeparators("-1000000"));
        EXPECT_EQ("-1,000,000,000", addThousandSeparators("-1000000000"));
        EXPECT_EQ("-1,012,789,345,456,123,678,456,345,809", addThousandSeparators("-1012789345456123678456345809"));
    }
    
    TEST (addThousandSeparators, changeThousandSeparator) {
        EXPECT_EQ("", addThousandSeparators("",'.'));
        EXPECT_EQ("-1", addThousandSeparators("-1",'.'));
        EXPECT_EQ("100", addThousandSeparators("100", '.'));
        EXPECT_EQ("-1.000", addThousandSeparators("-1000", '.'));
        EXPECT_EQ("-1.000.000.000", addThousandSeparators("-1000000000", '.'));
        EXPECT_EQ("1.012.789.345.456.123.678.456.345.809", addThousandSeparators("1012789345456123678456345809", '.'));
    }
    
    TEST (addThousandSeparators, changeDecimalSeparator) {
        EXPECT_EQ("0,5", addThousandSeparators("0.5",'.',','));
        EXPECT_EQ("0", addThousandSeparators("0",'.',','));
        EXPECT_EQ("-1,23", addThousandSeparators("-1.23",'.',','));
        EXPECT_EQ("100,56", addThousandSeparators("100.56", '.',','));
        EXPECT_EQ("-1.000,786", addThousandSeparators("-1000.786", '.',','));
        EXPECT_EQ("-1.000.000.000,4859", addThousandSeparators("-1000000000.4859", '.', ','));
        EXPECT_EQ("1.012.789.345.456.123.678.456.345.809,6785960", addThousandSeparators("1012789345456123678456345809.6785960", '.',','));
    }
    
    TEST (addThousandSeparators, changeSourceDecimalSeparator) {
        EXPECT_EQ("0,5", addThousandSeparators("0,5",'.',',',','));
        EXPECT_EQ("0", addThousandSeparators("0",'.',',',','));
        EXPECT_EQ("-1,23", addThousandSeparators("-1,23",'.',',',','));
        EXPECT_EQ("100,56", addThousandSeparators("100,56", '.',',',','));
        EXPECT_EQ("-1.000,786", addThousandSeparators("-1000,786", '.',',',','));
        EXPECT_EQ("-1.000.000.000,4859", addThousandSeparators("-1000000000,4859", '.', ',',','));
        EXPECT_EQ("1.012.789.345.456.123.678.456.345.809,6785960", addThousandSeparators("1012789345456123678456345809,6785960", '.',',',','));
    }
    

    【讨论】:

    • 这不适用于将分隔符放在不同数字之后的文化。例如,相同的数字在美国写为“10,000,000”,在印度写为“1,00,00,000”。
    【解决方案3】:

    注意:在提交此答案时,该帖子被标记为 C/C++。现在它被标记为 C。我怀疑它可能会再次改变。


    如果您想推出自己的使用 C99 的 C 解决方案,以下构成了在各种语言环境下在我的 Windows gcc 上工作的基础。

    #include <locale.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #define INT_STR_SIZE (CHAR_BIT*sizeof(int)*3/10 + 2)
    #define INT_SEP_STR_SIZE (INT_STR_SIZE * 3/2 + 1)
    #define INT_SEP(x) int_sep((char[INT_SEP_STR_SIZE]) { "" }, INT_SEP_STR_SIZE, x)
    
    char *int_sep(char *s, size_t sz, int x) {
      struct lconv *locale_ptr = localeconv();
      const char *grouping = locale_ptr->grouping;
      char sep = locale_ptr->thousands_sep[0];
    
      if (sz > 0) {
        int x0 = x;
        char *ptr = s + sz;
        *--ptr = '\0';
        char count = 0;
        do {
          if (count >= grouping[0]) {
            *--ptr = sep;
            if (grouping[1]) grouping++;
            count = 0;
          }
          count++;
          //printf("%d %d <%s> %p\n", count, n, locale_ptr->grouping, (void*)locale_ptr);
          *--ptr = (char) (abs(x % 10) + '0');
        } while (x /= 10);
        if (x0 < 0) {
          *--ptr = '-';
        }
        memmove(s, ptr, (size_t) (&s[sz] - ptr));
      }
      return s;
    }
    

    主要

    int main(void) {
      puts(setlocale(LC_ALL,"en_US"));
      printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_SEP_STR_SIZE), INT_SEP(12345678), INT_SEP(1234));
      printf(":%15s: :%15s: :%15s:\n", INT_SEP(-1), INT_SEP(0), INT_SEP(1));
      printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
      puts(setlocale(LC_ALL,"C"));
      printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
      puts(setlocale(LC_ALL,"it_IT"));
      printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
      puts(setlocale(LC_ALL,"as_IN"));
      printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
      puts(setlocale(LC_ALL,"be_BY"));
      printf(":%15s: :%15s: :%15s:\n", INT_SEP(INT_MIN), INT_SEP(INT_MIN+1), INT_SEP(INT_MAX));
      return 0;
    }
    

    输出

    en_US
    :             17: :     12,345,678: :          1,234:
    :             -1: :              0: :              1:
    : -2,147,483,648: : -2,147,483,647: :  2,147,483,647:
    C
    :    -2147483648: :    -2147483647: :     2147483647:
    it_IT
    : -2.147.483.648: : -2.147.483.647: :  2.147.483.647:
    as_IN
    :-2,14,74,83,648: :-2,14,74,83,647: : 2,14,74,83,647:
    be_BY
    : -2 147 483 648: : -2 147 483 647: :  2 147 483 647:
    

    【讨论】:

      【解决方案4】:

      为什么要重新发明轮子而不使用为此提供的功能?见GetNumberFormat

      自定义格式可以使用正确的NUMBERFMT 结构值来完成。例如(伪代码):

      //Display three digits after the decimal point.
      .NumDigits = 3
      //Display zeros after the decimal point.
      .LeadingZero = 1
      //Group every three digits to the left of the decimal.
      .Grouping = 3
      //Use a comma to as the decimal point (like they do in France and Spain).
      .lpDecimalSep = ","
      //Likewise, use a period as the grouping separator.
      .lpThousandSep = "."
      //Put the negative sign immediately after the number.
      .NegativeOrder = 3
      

      【讨论】:

        【解决方案5】:

        最后,我开发了自己的函数:

        std::string CFormat::GetInteger(int iValue)
        {
            std::string sValue;
            std::string sDot = ".";
            for(int iTmp = iValue; iTmp; iTmp /= 1000)
            {
                if ((int)(iTmp / 1000) == 0)
                    sDot.clear();
                sValue = sDot + SSTR(iTmp % 1000) + sValue;
            }
            if (sValue.empty())
                sValue = "0";
            return sValue;
        }
        

        我认为它更简单,它不依赖于 std::string 以外的其他类,我知道它可以在 Windows Mobile 设备中工作。

        【讨论】:

        • NMDV:通过使用do { ... } while (iTmp); 确保至少一次迭代,可以消除以下if (sValue.empty()) 测试。另外我认为这会穿插'-'-1234567 之类的值。
        • 感谢有关循环的建议。这仅限于正数。
        【解决方案6】:

        这是另一种方式,使用手动操作:

        #include <iostream>
        #include <string>
        #include <algorithm>
        
        int main()
        {
            int number = 123'456'789;
            auto src = std::to_string(number);
            auto dest = std::string();
        
            auto count = 3;
            for(auto i = src.crbegin() ; i != src.crend() ; ++i) {
                if (count == 0)
                {
                    dest.push_back(',');
                    count = 3;
                }
                if (count--) {
                    dest.push_back(*i);
                }
            }
            std::reverse(dest.begin(), dest.end());
        
            std::cout << dest << '\n';
        }
        

        【讨论】:

          【解决方案7】:

          这是一种方法 - 创建自定义语言环境并为其注入适当的自定义方面:

          #include <locale>
          #include <iostream>
          #include <memory>
          
          struct separate_thousands : std::numpunct<char> {
              char_type do_thousands_sep() const override { return ','; }  // separate with commas
              string_type do_grouping() const override { return "\3"; } // groups of 3 digit
          };
          
          int main()
          {
              int number = 123'456'789;
              std::cout << "default locale: " << number << '\n';
              auto thousands = std::make_unique<separate_thousands>();
              std::cout.imbue(std::locale(std::cout.getloc(), thousands.release()));
              std::cout << "locale with modified thousands: " << number << '\n';
          }
          

          预期输出:

          default locale: 123456789
          locale with modified thousands: 123,456,789
          

          【讨论】:

          • 更改行“自动千 = std::make_unique();”到“自动数千 = std::unique_ptr(new separate_thousands());”对于 C++11。
          猜你喜欢
          • 1970-01-01
          • 2012-03-11
          • 1970-01-01
          • 2018-11-17
          • 1970-01-01
          • 2020-10-16
          • 1970-01-01
          • 1970-01-01
          • 2012-11-25
          相关资源
          最近更新 更多