ofstream重置精度

8

我正在使用C++来操作文本文件。 我需要以一定的精度写入一些数字,所以我这样做:

    ofstrem file;
    file.open(filename, ios::app);
    file.precision(6);
    file.setf(ios::fixed, ios::floafield);
    //writing number on the file.....

现在我需要写其他内容,所以我需要重新设置精度。我该如何做?


你的标准库参考在哪里?ios_base::precision有两个重载,其中一个就是你要找的。 - Lightness Races in Orbit
3个回答

9

首先使用 precision() 方法检索流的原始精度值,将其存储,更改它,进行插入操作,然后将其更改回存储的值。

int main() {
   std::stringstream ss;
   ss << 1.12345 << " ";

   std::streamsize p = ss.precision();

   ss.precision(2);
   ss << 1.12345 << " ";

   ss.precision(p);
   ss << 1.12345;

   cout << ss.str();  // 1.12345 1.1 1.12345
}

实时演示。


抱歉,我没明白。我理解我需要做类似这样的事情:int prec=file.precision(); 但它不起作用。我不明白如何获取原始精度。 - andrea
只是另一件事...它还会重置file.setf(ios::fixed, ios::floafield);吗? - andrea
@andrea file.precision() 应该返回一个 int,稍后可以将其传递给 file.precision( newValue )。另一方面,您对精度所做的任何操作都不会影响标志;您需要对它们执行相同的操作。并确保在发生异常时恢复两者。 - James Kanze
类型是 std::streamsize。它可能是一个整数,但写成 std::streamsize,因为这就是它的类型。 - Lightness Races in Orbit
@andrea:它绝对“有效”;我已经向您展示了如何操作。 - Lightness Races in Orbit

4

有两种可能的解决方案。如果您处理的是一个大块输出,它使用相同的格式参数,您可以使用类似以下的内容:

class IOSave
{
    std::ios&           myStream;
    std::ios::fmtflags  myFlags;
    std::streamsize     myPrecision;
    char                myFill;
public:
    IOSave( std::ios& userStream )
        : myStream( userStream )
        , myFlags( userStream.flags() )
        , myPrecision( userStream.precision() )
        , myFill( userStream.fill() )
    {
    }
    ~IOSave()
    {
        myStream.flags( myFlags );
        myStream.precision( myPrecision );
        myStream.fill( myFill );
    }
};

在输出代码块的顶部定义一个实例即可。

然而,大多数情况下,我会定义自己的操作器,其派生自以下内容:

class StateSavingManipulator
{
    mutable std::ios*          myStream;
    mutable std::ios::fmtflags mySavedFlags;
    mutable int                mySavedPrec;
    mutable char               mySavedFill;

    virtual void               setState( std::ios& stream ) const = 0 ;
protected:
    StateSavingManipulator();
public:
    virtual                   ~StateSavingManipulator();
    void                       operator()( std::ios& stream ) const ;
};

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 ;
}

实现起来有点棘手,因为您必须考虑到如果在同一个表达式中使用了多个操作器,编译器可以按照任意顺序构造它们(因此也会按照任意顺序销毁它们)。所以:

namespace {
    int getXAlloc() ;
    int ourXAlloc = getXAlloc() + 1 ;

    int getXAlloc()
    {
        if ( ourXAlloc == 0 ) {
            ourXAlloc = std::ios::xalloc() + 1 ;
            assert( ourXAlloc != 0 ) ;
        }
        return ourXAlloc - 1 ;
    }
}

StateSavingManipulator::StateSavingManipulator()
    : myStream( NULL )
{
}

StateSavingManipulator::~StateSavingManipulator()
{
    if ( myStream != NULL ) {
        myStream->flags( mySavedFlags ) ;
        myStream->precision( mySavedPrec ) ;
        myStream->fill( mySavedFill ) ;
        myStream->pword( getXAlloc() ) = NULL ;
    }
}

void StateSavingManipulator::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 ) ;
}

派生出的操纵器然后在其实现setState时执行必须进行的任何操作。有了这个,你可以写出像这样的东西:
std::cout << FFmt( 6, 2 ) << someValue << std::endl;

无需担心保存和恢复格式状态。


1
一个解决方案:
std::streamsize oldPres = file.precision(2);
file.setf(ios::fixed, ios::floafield);
… code continues …
file.precision(oldPres);
file.unsetf(std::ios::fixed);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接