Std :: cout durumunu işledikten sonra geri yükleyin


105

Şöyle bir kodum olduğunu varsayalım:

void printHex(std::ostream& x){
    x<<std::hex<<123;
}
..
int main(){
    std::cout<<100; // prints 100 base 10
    printHex(std::cout); //prints 123 in hex
    std::cout<<73; //problem! prints 73 in hex..
}

Sorum şu cout, işlevden döndükten sonra durumunu orijinal haline 'geri yüklemenin' herhangi bir yolu olup olmadığı ? (Biraz gibi std::boolalphave std::noboolalpha..)?

Teşekkürler.


Büyünün sadece bir sonraki vardiya operasyonu için sürdüğüne inanıyorum. Değişiklik yalnızca biçim bayraklarını manipülatör kullanmak yerine manuel olarak değiştirirseniz kalıcı olur.
Billy ONeal

4
@BillyONeal: Hayır, manipülatör kullanmak, biçim bayraklarını manuel olarak değiştirmekle aynı etkiye sahiptir. :-P
Chris Jester-Young

3
Gizli bir bulgu Ostream formatı geri yüklenmiyor (STREAM_FORMAT_STATE) nedeniyle buradaysanız , bkz. Coverity find: Ostream formatı geri yüklenmiyor (STREAM_FORMAT_STATE) .
jww

Benzer bir şey yaptım - Kod İnceleme ile ilgili soruma bakın: Standart bir akış kullanın ve daha sonra ayarlarını geri yükleyin .
Toby Speight

1
Bu soru, iostream'in neden standarttan daha iyi olmadığının mükemmel bir örneğidir. Sadece / yarı / tam / ne kalıcı olmayan iomanip yüzünden iki kötü böcek buldum.
fuujuhi

Yanıtlar:


97

yapmanız gereken #include <iostream>veya #include <ios>daha sonra gerektiğinde:

std::ios_base::fmtflags f( cout.flags() );

//Your code here...

cout.flags( f );

Bunları işlevinizin başına ve sonuna koyabilir veya bunu RAII ile nasıl kullanacağınıza dair bu yanıtı kontrol edebilirsiniz .


5
@ ChrisJester-Young, aslında iyi olan C ++ RAII'dir, özellikle bunun gibi bir durumda!
Alexis Wilke

4
@Alexis% 100 katılıyorum. Cevabımı gör (Boost IO Stream State Saver). :-)
Chris Jester-Young

3
Bu istisnai güvenli değildir.
einpoklum

2
Bayrakların yanı sıra akış durumunda daha fazlası var.
jww

3
Biçimleri akışlara aktarmayarak sorunu önleyebilirsiniz. Biçimi ve verileri geçici bir
dizgi akışı

63

Boost IO Akış Devlet Tasarrufu tam olarak neye ihtiyacınız görünüyor. :-)

Kod pasajınıza dayalı örnek:

void printHex(std::ostream& x) {
    boost::io::ios_flags_saver ifs(x);
    x << std::hex << 123;
}

1
Burada sihir olmadığına dikkat edin, bu ios_flags_savertemelde bayrakları @ StefanKendall'ın cevabındaki gibi kaydeder ve ayarlar.
einpoklum

15
@einpoklum Ama diğer cevabın aksine istisnai güvenli. ;-)
Chris Jester-Young

2
Bayrakların yanı sıra akış durumunda daha fazlası var.
jww

4
@jww IO akım halinde Koruyucu kütüphanesi olan akım halinde, farklı bölümlerini kaydetmek için, birden çok sınıflar vardır ios_flags_saversadece bir tanesidir.
Chris Jester-Young

3

45

Burada sunulan yanıtların tam durumunu geri yüklemeyeceğini unutmayın std::cout. Örneğin, std::setfillaradıktan sonra bile "takılı kalacaktır" .flags(). Daha iyi bir çözüm kullanmaktır .copyfmt:

std::ios oldState(nullptr);
oldState.copyfmt(std::cout);

std::cout
    << std::hex
    << std::setw(8)
    << std::setfill('0')
    << 0xDECEA5ED
    << std::endl;

std::cout.copyfmt(oldState);

std::cout
    << std::setw(15)
    << std::left
    << "case closed"
    << std::endl;

Yazdırılacak:

case closed

ziyade:

case closed0000

İlk sorum birkaç yıl önce cevaplanmış olsa da, bu cevap harika bir katkı. :-)
UltraInstinct

2
@UltraInstinct Daha iyi bir çözüm gibi görünüyor, bu durumda, bunun yerine kabul edilen cevap olabilir ve muhtemelen yapmalısınız.
underscore_d

Bu, bazı nedenlerden dolayı, akış için istisnalar etkinleştirilirse istisna atar. coliru.stacked-crooked.com/a/2a4ce6f5d3d8925b
anton_rh

1
Görünüşe göre std::iosbu her zaman kötü durumda çünkü NULLrdbuf var. Bu nedenle, istisnaların etkinleştirildiği bir durumun ayarlanması, kötü durum nedeniyle istisna atılmasına neden olur. Çözümler: 1) set yerine bazı sınıflar (örneğin std::stringstream) rdbufkullanın std::ios. 2) İstisnaların durumunu yerel değişkene ayrı olarak kaydedin ve state.copyfmtdaha önce bunları devre dışı bırakın , ardından değişkendeki istisnayı geri yükleyin (ve oldStateistisnaların devre dışı bırakıldığı durumu geri yükledikten sonra bunu tekrar yapın ). 3) Ayar rdbufiçin std::iosbu gibi:struct : std::streambuf {} sbuf; std::ios oldState(&sbuf);
anton_rh

22

Bu yanıttaki örnek kodu kullanarak bir RAII sınıfı oluşturdum. Bu tekniğin en büyük avantajı, bir iostream'de bayrakları ayarlayan bir işlevden birden çok dönüş yolunuz varsa ortaya çıkar. Hangi dönüş yolu kullanılırsa kullanılsın, yıkıcı her zaman çağrılacak ve bayraklar her zaman sıfırlanacaktır. İşlev döndüğünde bayrakları geri yüklemeyi unutma şansı yoktur.

class IosFlagSaver {
public:
    explicit IosFlagSaver(std::ostream& _ios):
        ios(_ios),
        f(_ios.flags()) {
    }
    ~IosFlagSaver() {
        ios.flags(f);
    }

    IosFlagSaver(const IosFlagSaver &rhs) = delete;
    IosFlagSaver& operator= (const IosFlagSaver& rhs) = delete;

private:
    std::ostream& ios;
    std::ios::fmtflags f;
};

Daha sonra, mevcut bayrak durumunu kaydetmek istediğinizde yerel bir IosFlagSaver örneği oluşturarak onu kullanırsınız. Bu örnek kapsam dışına çıktığında bayrak durumu geri yüklenecektir.

void f(int i) {
    IosFlagSaver iosfs(std::cout);

    std::cout << i << " " << std::hex << i << " ";
    if (i < 100) {
        std::cout << std::endl;
        return;
    }
    std::cout << std::oct << i << std::endl;
}

2
Harika, birisi fırlatırsa, yayınınızda hala doğru bayrakları görebilirsiniz.
Alexis Wilke

4
Bayrakların yanı sıra akış durumunda daha fazlası var.
jww

1
C ++ 'ın denemeye izin vermesini gerçekten isterdim. Bu, RAII'nin çalıştığı mükemmel bir örnektir, ancak sonunda daha basit olabilirdi.
Ticaret-Fikirler Philip

2
Projeniz en azından biraz mantıklıysa, Boost'a sahipsiniz ve bu amaç için durum koruyucularla birlikte geliyor .
Oca Hudec

9

Çıktıyı daha okunaklı hale getirmek için biraz değişiklik yaparak:

void printHex(std::ostream& x) {
   ios::fmtflags f(x.flags());
   x << std::hex << 123 << "\n";
   x.flags(f);
}

int main() {
    std::cout << 100 << "\n"; // prints 100 base 10
    printHex(std::cout);      // prints 123 in hex
    std::cout << 73 << "\n";  // problem! prints 73 in hex..
}

9

Stdout arabelleği etrafında başka bir sarmalayıcı oluşturabilirsiniz:

#include <iostream>
#include <iomanip>
int main() {
    int x = 76;
    std::ostream hexcout (std::cout.rdbuf());
    hexcout << std::hex;
    std::cout << x << "\n"; // still "76"
    hexcout << x << "\n";   // "4c"
}

Bir işlevde:

void print(std::ostream& os) {
    std::ostream copy (os.rdbuf());
    copy << std::hex;
    copy << 123;
}

Elbette, performans bir sorunsa, bu biraz daha pahalıdır, çünkü iosyerel ayar gibi para ödediğiniz ancak kullanamayacağınız bazı şeyler dahil olmak üzere tüm nesneyi (ancak arabelleği değil) kopyalamaktır .

Aksi takdirde, eğer .flags()kullanacaksanız .setf(), <<sözdizimi yerine tutarlı olmanın ve kullanmanın daha iyi olduğunu düşünüyorum (saf stil sorunu).

void print(std::ostream& os) {
    std::ios::fmtflags os_flags (os.flags());
    os.setf(std::ios::hex);
    os << 123;
    os.flags(os_flags);
}

Diğerleri söylediler Yukarıdaki koymak (ve edebilir .precision()ve .fill()ancak tipik genellikle modifiye edilecek ve daha ağır değildir değil yerel ve kelimeler alakalı şeyler) bir sınıfta kolaylık ve özel durum korumalı hale getirmek için; kurucu kabul etmelidir std::ios&.


İyi bir nokta [+], ama tabii ki Mark Sherred'in de belirttiğistd::stringstream gibi biçimlendirme kısmı için kullanmayı hatırlıyor .
Kurt

@Wolf Ne demek istediğini anladığımdan emin değilim. An std::stringstream , an'dır std:ostream, ancak birinin kullanılması fazladan bir ara tampon sağlar.
n.caillou

Elbette her ikisi de çıktıyı biçimlendirmek için geçerli yaklaşımlardır, her ikisi de bir akış nesnesi sunar, tanımladığınız şey benim için yeni. Artıları ve eksileri şimdi düşünmem gerekiyor. Bununla birlikte, aydınlatıcı cevapları olan ilham verici bir soru ... (Akışın kopyası varyantını kastediyorum)
Wolf

1
Bir akışı kopyalayamazsınız, çünkü tamponları kopyalamak genellikle mantıklı değildir (örneğin, stdout). Ancak, aynı tampon için birkaç akış nesnesine sahip olabilirsiniz, bu yanıtın yapmayı önerdiği şey budur. Oysa bir std:stringstreamirade kendi bağımsızlığını std:stringbuf(bir std::streambuftürevini) yaratır ve bu daha sonrastd::cout.rdbuf()
n.caillou

Açıklama için teşekkürler.
Kurt

0

Qbert220'nin cevabını bir şekilde genellemek istiyorum:

#include <ios>

class IoStreamFlagsRestorer
{
public:
    IoStreamFlagsRestorer(std::ios_base & ioStream)
        : ioStream_(ioStream)
        , flags_(ioStream_.flags())
    {
    }

    ~IoStreamFlagsRestorer()
    {
        ioStream_.flags(flags_);
    }

private:
    std::ios_base & ioStream_;
    std::ios_base::fmtflags const flags_;
};

Bu, giriş akışları ve diğerleri için de çalışmalıdır.

Not: Bunu yukarıdaki cevaba basitçe bir yorum yapmak isterdim, ancak stackoverflow eksik itibar nedeniyle bunu yapmama izin vermiyor. Bu yüzden basit bir yorum yerine cevapları burada karıştırıyorum ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.