【问题标题】:How to insert spaces in a big number to make it more readable?如何插入大量空格以使其更具可读性?
【发布时间】:2011-11-07 15:33:03
【问题描述】:

我想出了这个,因为 stackoverflow 上提供的其他示例是用 C# 编写的

string number_fmt(ulong n)
{
    // cout << "(" << n << ")" << endl;
    char s[128];
    sprintf(s, "%lu", n);
    string r(s);
    reverse(r.begin(), r.end());
    int space_inserted = 0;
    size_t how_many_spaces = r.length() / 3;

    if(r.length() % 3 != 0)
        how_many_spaces += 1;

    for(int i = 1; i < how_many_spaces; ++i)
    {
        r.insert(3 * i + space_inserted, " ");
        space_inserted += 1;
    }
    reverse(r.begin(), r.end());

    return r;
}

你知道更好的解决方案吗?

【问题讨论】:

  • 这个有什么不好?这将有助于选择更好的
  • 必须是空格吗?您可以尝试使用&lt;locale&gt; 标头功能:cplusplus.com/forum/beginner/24707
  • 是的。流已经有一个本地对象,它将为您格式化数字。手动操作不是对称的(通过使用本地,您可以自动获得对称的输入和输出(无需工作))。问题是默认情况下应用程序位于“C”本地。只需将机器默认设置为本地 (""),它就会开始为您的语言环境正确格式化数字。

标签: c++ string numbers


【解决方案1】:

这个是不同的,但更好的是主观的。我认为它非常简洁明了,但它在做什么:

string number_fmt(unsigned long long n, char sep = ',') {
    stringstream fmt;
    fmt << n;
    string s = fmt.str();
    s.reserve(s.length() + s.length() / 3);

    // loop until the end of the string and use j to keep track of every
    // third loop starting taking into account the leading x digits (this probably
    // can be rewritten in terms of just i, but it seems more clear when you use
    // a seperate variable)
    for (int i = 0, j = 3 - s.length() % 3; i < s.length(); ++i, ++j)
        if (i != 0 && j % 3 == 0)
            s.insert(i++, 1, sep);

    return s;
}

像这样使用它

cout << number_fmt(43615091387465) << endl;

打印

43,615,091,387,465

【讨论】:

  • 这是个坏主意。流上的本地对象不仅已经做到了,而且在如何做到这一点上更加灵活。还可以使用您输入/输出的本地对象,它是对称的(即 ir 将用逗号打印并用逗号回读。此外,通过使用本地,您可以获得本地特定且不限于特定样式的打印数字(在此案例欧洲-盎格鲁)。
【解决方案2】:

如果“更好”意味着更高效,您应该:

  1. 在输出字符串上使用reserve(你知道它的大小...)

  2. 避免在字符串中间使用insert,因为每次这样做时都必须复制大部分字符串。

我会这样说(未经测试):

std::string number_fmt (ulong n)
{
  std::ostringstream buff;
  buff << n;
  std::string without_spaces = buff.str ();
  std::string with_spaces;
  with_spaces.reserve ((without_spaces.size () * 4) / 3);
  std::size_t nb_inserted = 0;

  for (auto it = without_spaces.rbegin (); it != without_spaces.rend (); ++it)
  {
     if (nb_inserted % 3  ==  0  &&  nb_inserted != 0)
     {
         with_spaces.push_back (' ');
     }
     ++ nb_inserted;

     with_spaces.push_back (*it);   
  }
  std::reverse (with_spaces.begin (), with_spaces.end ());
  return with_spaces;
}

【讨论】:

    【解决方案3】:

    我不知道“更好”,但是这个版本使用std::locale等。

    #include <iostream>
    #include <locale>
    #include <sstream>
    
    template<class Char>
    class MyFacet : public std::numpunct<Char> {
    public:
      std::string do_grouping() const { return "\3"; }
      Char do_thousands_sep() const { return ' '; }
    };
    
    std::string number_fmt(unsigned long n)
    {
      std::ostringstream oss;
      oss.imbue(std::locale(oss.getloc(), new MyFacet<char>));
      oss << n;
      return oss.str();
    }
    
    int main() {
      std::cout << number_fmt(123456789) << "\n";
    }
    


    编辑:当然,如果您的最终目标是在ostream 上打印值,您可以完全跳过将它们存储在string 中。
    #include <iostream>
    #include <locale>
    #include <sstream>
    #include <cwchar>
    
    template <class Char>
    class MyFacet : public std::numpunct<Char> {
    public:
      std::string do_grouping() const { return "\3"; }
      Char do_thousands_sep() const { return ' '; }
    };
    
    int main(int ac, char **av) {
      using std::locale;
      using std::cout;
    
      // Show how it works to start with
      cout << 123456789 << "\n";
    
      // Switch it to spacey mode
      locale oldLoc =
        cout.imbue(locale(cout.getloc(), new MyFacet<char>));
    
      // How does it work now?
      cout << 456789123 << "\n";
    
      // You probably want to clean up after yourself
      cout.imbue(oldLoc);
    
      // Does it still work?
      cout << 789123456 << "\n";
    }
    

    【讨论】:

    • 是的使用本地,但为什么要使用字符串流?
    • @Tux - 因为原始问题的范围是将数字转换为字符串,而不是将数字发送到标准输出。我将更新我的答案以突出更简单的替代方案。
    【解决方案4】:

    诚然,如果想要获得最高效的版本并且不介意针对手头的情况对其进行专门化,那么使用本地 char 缓冲区会大有帮助。

    #include <iostream>
    #include <string>
    
    std::string format(unsigned long long i) {
      char buffer[128]; // can be adapted more tightly with std::numeric_limits
    
      char* p = buffer + 128;
      *(--p) = '\0';
    
      unsigned char count = 0;
      while (i != 0) {
        *(--p) = '0' + (i % 10);
        i /= 10;
    
        if (++count == 3) { count = 0; *(--p) = ' '; }
      }
    
      return p;
    }
    
    int main() {
      std::cout << format(1234567890) << '\n';
    }
    

    ideone行动中:

    1 234 567 890
    

    (关键点:数字打印,往后走

    【讨论】:

      【解决方案5】:

      这已由语言环境完成。

      默认本地是“C”,不进行格式化。但您可以将其设置为当前特定语言的本地(由您的计算机设置定义,将当前本地设置为main 的第一行)。

      int main()
      {
          std::locale::global(std::locale("")); // Set the default local of the machine
                                                // Will be used by all streams.
                                                // The "" will find the machine specific local
                                                // and use that instead of the "C" locale
                                                // Note: The C local should only be used for programmers.
      
          // Alternatively you can imbue particular stream with the local
          // To achieve a localized effect
          // std::cout.imbue(std::locale(""));
      
          // Now all you do is print the number.
          std::cout << "123456789\n";  // This will print the number according to your local
      }                                // For me US-en this is   123,456,789
                                       // Your may very.
      

      如果你想明确地做某事,那么你可以在本地设置一个方面来打印数字。

      #include <iostream>
      #include <locale>
      #include <string>
      
      
      template<typename CharT>
      struct Sep : public std::numpunct<CharT>
      {
              virtual std::string do_grouping()      const   {return "\003";}
              virtual CharT       do_thousands_sep() const   {return ':';}
      };
      
      int main()
      {
              std::cout.imbue(std::locale(std::cout.getloc(), new Sep <char>()));
      
              std::cout << 123456789 << "\n";   // this prints 123:456:789
      }
      

      【讨论】:

        【解决方案6】:

        不是很理想,但很小

        QString str = QString::number(d);
        for (int i = 3; str.size() > i; i += 4)
           str.insert(str.size() - i, ' ');
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-11-06
          • 2019-06-05
          • 2020-12-22
          • 1970-01-01
          • 1970-01-01
          • 2012-06-14
          相关资源
          最近更新 更多