【问题标题】:Effective use of C++ iomanip library有效使用 C++ iomanip 库
【发布时间】:2011-07-16 18:01:44
【问题描述】:

我在 C++ 中创建了一个Vector 类,它非常适合我的问题。我现在正在清理它,我遇到了以下代码:

std::ostream& operator<<(std::ostream &output, const Vector &v){
  output<<"["
    <<std::setiosflags(std::ios::right | std::ios::scientific)
    <<std::setw(23)
    <<std::setprecision(16)
    <<v._x<<", "
    <<std::setiosflags(std::ios::right | std::ios::scientific)
    <<std::setw(23)
    <<std::setprecision(16)
    <<v._y<<", "
    <<std::setiosflags(std::ios::right | std::ios::scientific)
    <<std::setw(23)
    <<std::setprecision(16)
    <<v._z<<"]";
  return output;
} 

代码允许将向量打印为std::cout&lt;&lt;v&lt;&lt;std::endl;。每个数字有 23 个空格,其中 16 个是小数。文本右对齐以便打印:

 1.123456123456e+01
-1.123456123456e+01

代替

1.123456123456e+01
-1.123456123456e+01

代码似乎非常重复。您如何“存储”格式(所有setiosflagssetwsetprecision 语句),以便您可以说“以标准方式打印字符,但以这种给定格式打印数字”。

谢谢!

编辑

根据 Rob Adams 的评论,我将丑陋的代码(正如其他人所指出的那样,会弄乱“下一个人”的精确度)变得更简洁(并且正确):

std::ostream& operator<<(std::ostream &output, const Vector &v){
  std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
  std::streamsize p = output.precision(16);
  output<<"["
    <<std::setw(23)<<v._x<<", "
    <<std::setw(23)<<v._y<<", "
    <<std::setw(23)<<v._z
    <<"]";
  output.flags(f);
  output.precision(p);
  return output;
}

【问题讨论】:

标签: c++ stream iostream iomanip


【解决方案1】:

只有std::setw() 是临时的。另外两个调用 setiosflagssetprecision 具有永久效果。

因此,您可以将代码更改为:

std::ostream& operator<<(std::ostream &output, const Vector &v){
  output<<"["
    <<std::setiosflags(std::ios::right | std::ios::scientific)
    <<std::setw(23)
    <<std::setprecision(16)
    <<v._x<<", "
    <<std::setw(23)
    <<v._y<<", "
    <<std::setw(23)
    <<v._z<<"]";
  return output;
} 

但是现在你已经为下一个人破坏了旗帜和精确度。试试这个:

std::ostream& operator<<(std::ostream &output, const Vector &v){
  std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
  std::streamsize p = output.precision(16);
  output<<"["
    <<std::setw(23)
    <<v._x<<", "
    <<std::setw(23)
    <<v._y<<", "
    <<std::setw(23)
    <<v._z<<"]";
  output.flags(f);
  output.precision(p);
  return output;
} 

最后,如果你一定要去掉重复的常量23,你可以这样做(但我不推荐):

struct width {
  int w;
  width(int w) : w(w) {}
  friend std::ostream& operator<<(std::ostream&os, const width& w) {
    return os << std::setw(width.w);
  }
};


std::ostream& operator<<(std::ostream &output, const Vector &v){
  std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
  std::streamsize p = output.precision(16);
  width w(23);
  output<<"["
    <<w
    <<v._x<<", "
    <<w
    <<v._y<<", "
    <<w
    <<v._z<<"]";
  output.flags(f);
  output.precision(p);
  return output;
} 

另请参阅this other question,他们认为您不能永久设置宽度。

【讨论】:

  • 非常感谢 - 你不仅解决了我的问题,还教会了我一些关于一般流概念的知识。谢谢!
【解决方案2】:

在 C++20 中,您将能够做到:

std::ostream& operator<<(std::ostream& output, const Vector& v){
  const int width = 23, precision = 16;
  return output << std::format(
      "[{0:{3}.{4}e}, {1:{3}.{4}e}, {2:{3}.{4}e}]",
      v._x, v._y, v._z, width, precision);
} 

与 I/O 操纵器不同,std::format 不会改变 ostream 的格式化状态,从而避免了 Bo Persson 提到的问题:

你真正的问题是这个之后的下一个输出会发生什么......

它也可以使 I/O 操纵器与 Vector 一起正常工作。

std::format 可用之前,您可以使用它所基于的the {fmt} library

【讨论】:

    【解决方案3】:

    除了 setw() 之外的所有东西实际上已经这样做了。它们是“粘性的”。

    你真正的问题是这个之后的下一个输出会发生什么......

    【讨论】:

      【解决方案4】:

      通常,您不会直接使用标准操纵器。在 例如,在这种情况下,您可以定义一个操纵器 fromVector,并使用它:

      output << '['
             << fromVector << v.x << ", "
             << fromVector << v.y << ", "
             << fromVector << v.z << ']';
      

      这样,如果你想改变宽度和精度 向量中的元素,你只需要在一个地方做。

      在这种情况下,操纵器没有参数,所有这些 需要一个简单的函数:

      std::ostream& fromVector(std::ostream& stream)
      {
          stream.setf(std::ios::right, std::ios::adjustfield);
          stream.setf(std::ios::scientific, std::ios::floatfield);
          stream.precision(16);
          stream.width(32);
          return stream;
      }
      

      当然,这将改变任何格式的大部分格式 后来的用户。一般来说,最好使用 保存状态并恢复状态的临时类对象 在析构函数中。我通常从 类似:

      标题: StateSavingManip 类 { 民众: StateSavingManip( StateSavingManip const& 其他 ) ;

          virtual             ~StateSavingManip() ;
          void                operator()( std::ios& stream ) const ;
      
      protected:
                              StateSavingManip() ;
      
      private:
          virtual void        setState( std::ios& stream ) const = 0 ;
      
      private:
          StateSavingManip&   operator=( StateSavingManip const& ) ;
      
      private:
          mutable std::ios*   myStream ;
          mutable std::ios::fmtflags
                              mySavedFlags ;
          mutable int         mySavedPrec ;
          mutable char        mySavedFill ;
      } ;
      
      inline std::ostream&
      operator<<(
          std::ostream&       out,
          StateSavingManip const&
                              manip )
      {
          manip( out ) ;
          return out ;
      }
      
      inline std::istream&
      operator>>(
          std::istream&       in,
          StateSavingManip const&
                              manip )
      {
          manip( in ) ;
          return in ;
      }
      

      来源: int getXAlloc() ; int ourXAlloc = getXAlloc() + 1 ;

      int
      getXAlloc()
      {
          if ( ourXAlloc == 0 ) {
              ourXAlloc = std::ios::xalloc() + 1 ;
              assert( ourXAlloc != 0 ) ;
          }
          return ourXAlloc - 1 ;
      }
      }
      
      StateSavingManip::StateSavingManip()
          :   myStream( NULL )
      {
      }
      
      StateSavingManip::StateSavingManip(
          StateSavingManip const&
                              other )
      {
          assert( other.myStream == NULL ) ;
      }
      
      StateSavingManip::~StateSavingManip()
      {
          if ( myStream != NULL ) {
              myStream->flags( mySavedFlags ) ;
              myStream->precision( mySavedPrec ) ;
              myStream->fill( mySavedFill ) ;
              myStream->pword( getXAlloc() ) = NULL ;
          }
      }
      
      void
      StateSavingManip::operator()( 
          std::ios&           stream ) const
      {
          void*&              backptr = stream.pword( getXAlloc() ) ;
          if ( backptr == NULL ) {
              backptr      = const_cast< StateSavingManip* >( this ) ;
              myStream     = &stream ;
              mySavedFlags = stream.flags() ;
              mySavedPrec  = stream.precision() ;
              mySavedFill  = stream.fill() ;
          }
          setState( stream ) ;
      }
      

      如果你这样做,你必须在后面加上括号 机械手,例如:

      output << '['
             << fromVector() << v.x << ", "
             << fromVector() << v.y << ", "
             << fromVector() << v.z << ']';
      

      (我相信一些聪明的灵魂会想出一个办法 避开他们,但他们从来没有打扰过我,所以我没有 打扰了。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-04
        • 1970-01-01
        • 1970-01-01
        • 2021-09-17
        • 1970-01-01
        相关资源
        最近更新 更多