ตัวจัดการ iomanip ใดที่ 'เหนียว'?


140

ฉันเพิ่งมีปัญหาในการสร้างstringstreamอันเนื่องมาจากความจริงที่ว่าฉันคิดว่าไม่ถูกต้องstd::setw()จะส่งผลกระทบต่อสายน้ำสำหรับการแทรกทุกครั้งจนกว่าฉันจะเปลี่ยนมันอย่างชัดเจน อย่างไรก็ตามจะไม่มีการตั้งค่าเสมอหลังจากการแทรก

// With timestruct with value of 'Oct 7 9:04 AM'
std::stringstream ss;
ss.fill('0'); ss.setf(ios::right, ios::adjustfield);
ss << setw(2) << timestruct.tm_mday;
ss << timestruct.tm_hour;
ss << timestruct.tm_min;
std::string filingTime = ss.str(); // BAD: '0794'

ดังนั้นฉันมีคำถามจำนวนมาก:

  • ทำไมจึงเป็นsetw()เช่นนี้?
  • มีผู้ควบคุมอื่น ๆ ด้วยวิธีนี้หรือไม่?
  • มีความแตกต่างในพฤติกรรมระหว่างstd::ios_base::width()และstd::setw()?
  • ในที่สุดก็มีการอ้างอิงออนไลน์ที่ชัดเจนเอกสารพฤติกรรมนี้ เอกสารผู้ขายของฉัน (MS Visual Studio 2005) ดูเหมือนจะไม่แสดงสิ่งนี้อย่างชัดเจน

รอบการทำงานอยู่ที่นี่: stackoverflow.com/a/37495361/984471
Manohar Reddy Poreddy

คำตอบ:


87

หมายเหตุสำคัญจากความคิดเห็นด้านล่าง:

โดย Martin:

@Chareles: จากนั้นข้อกำหนดนี้ผู้ควบคุมการทั้งหมดจะเหนียว ยกเว้น setw ซึ่งดูเหมือนว่าจะถูกรีเซ็ตหลังการใช้งาน

โดย Charles:

แน่นอน! และเหตุผลเดียวที่ setw ดูเหมือนจะทำงานแตกต่างกันคือเนื่องจากมีข้อกำหนดในการดำเนินการส่งออกที่จัดรูปแบบเพื่อ. bandwidth (0) อย่างชัดเจนกระแส

ต่อไปนี้คือการอภิปรายที่นำไปสู่ข้อสรุปข้างต้น:


เมื่อดูที่โค้ดผู้ควบคุมต่อไปนี้จะส่งคืนออบเจ็กต์แทนที่จะเป็นสตรีม

setiosflags
resetiosflags
setbase
setfill
setprecision
setw

นี่เป็นเทคนิคทั่วไปในการใช้การดำเนินการกับวัตถุถัดไปที่ใช้กับสตรีมเท่านั้น น่าเสียดายที่นี่ไม่ได้ขัดขวางพวกเขาจากการเป็นเหนียว การทดสอบระบุว่าพวกเขาทั้งหมดยกเว้นsetwเหนียว

setiosflags:  Sticky
resetiosflags:Sticky
setbase:      Sticky
setfill:      Sticky
setprecision: Sticky

เครื่องมือปรับแต่งอื่น ๆ ทั้งหมดจะส่งคืนวัตถุสตรีม ดังนั้นข้อมูลสถานะใด ๆ ที่พวกเขาเปลี่ยนแปลงจะต้องถูกบันทึกไว้ในวัตถุกระแสและดังนั้นจึงถาวร (จนกว่าตัวควบคุมอื่นจะเปลี่ยนสถานะ) ดังนั้นตัวจัดการต่อไปนี้จะต้องเป็นตัวจัดการเหนียว

[no]boolalpha
[no]showbase
[no]showpoint
[no]showpos
[no]skipws
[no]unitbuf
[no]uppercase

dec/ hex/ oct

fixed/ scientific

internal/ left/ right

ตัวจัดการเหล่านี้ทำการดำเนินการกับกระแสข้อมูลเองแทนที่จะเป็นวัตถุกระแส (แม้ว่าในทางเทคนิคแล้วกระแสข้อมูลเป็นส่วนหนึ่งของสถานะวัตถุกระแส) แต่ฉันไม่เชื่อว่าพวกเขาส่งผลกระทบต่อส่วนอื่น ๆ ของสถานะวัตถุกระแส

ws/ endl/ ends/ flush

ข้อสรุปก็คือว่า setw น่าจะเป็นคนเดียวที่เป็นหุ่นยนต์ในเวอร์ชั่นของฉันที่ไม่เหนียวเหนอะ

สำหรับชาร์ลส์เคล็ดลับง่าย ๆ ที่จะส่งผลกระทบต่อไอเท็มถัดไปเท่านั้นในโซ่:
นี่คือตัวอย่างวิธีที่วัตถุสามารถใช้ในการเปลี่ยนแปลงสถานะชั่วคราวจากนั้นจึงนำกลับมาใช้โดยการใช้วัตถุ:

#include <iostream>
#include <iomanip>

// Private object constructed by the format object PutSquareBracket
struct SquareBracktAroundNextItem
{
    SquareBracktAroundNextItem(std::ostream& str)
        :m_str(str)
    {}
    std::ostream& m_str;
};

// New Format Object
struct PutSquareBracket
{};

// Format object passed to stream.
// All it does is return an object that can maintain state away from the
// stream object (so that it is not STICKY)
SquareBracktAroundNextItem operator<<(std::ostream& str,PutSquareBracket const& data)
{
    return SquareBracktAroundNextItem(str);
}

// The Non Sticky formatting.
// Here we temporariy set formating to fixed with a precision of 10.
// After the next value is printed we return the stream to the original state
// Then return the stream for normal processing.
template<typename T>
std::ostream& operator<<(SquareBracktAroundNextItem const& bracket,T const& data)
{
    std::ios_base::fmtflags flags               = bracket.m_str.flags();
    std::streamsize         currentPrecision    = bracket.m_str.precision();

    bracket.m_str << '[' << std::fixed << std::setprecision(10) << data << std::setprecision(currentPrecision) << ']';

    bracket.m_str.flags(flags);

    return bracket.m_str;
}


int main()
{

    std::cout << 5.34 << "\n"                        // Before 
              << PutSquareBracket() << 5.34 << "\n"  // Temp change settings.
              << 5.34 << "\n";                       // After
}


> ./a.out 
5.34
[5.3400000000]
5.34

แผ่นโกงที่ดี เพิ่มการอ้างอิงถึงที่มาของข้อมูลและมันจะเป็นคำตอบที่สมบูรณ์แบบ
Mark Ransom

1
อย่างไรก็ตามฉันสามารถตรวจสอบว่า setfill () เป็นจริง 'เหนียว' แม้ว่ามันจะส่งคืนวัตถุ ดังนั้นฉันคิดว่าคำตอบนี้ไม่ถูกต้อง
John K

2
วัตถุที่คืนค่ากระแสต้องเหนียวในขณะที่คืนค่าวัตถุอาจเหนียว แต่ไม่จำเป็น ฉันจะอัปเดตคำตอบด้วยข้อมูลของจอห์น
Martin York

1
ฉันไม่แน่ใจว่าฉันเข้าใจเหตุผลของคุณ เครื่องมือปรับแต่งทั้งหมดที่รับพารามิเตอร์จะถูกนำมาใช้เป็นฟังก์ชั่นอิสระที่ส่งคืนวัตถุที่ไม่ระบุที่ทำหน้าที่ในกระแสเมื่อวัตถุนั้นถูกแทรกลงในกระแสเพราะมันเป็นวิธีเดียว (?) วิธีการรักษาไวยากรณ์การแทรกด้วยพารามิเตอร์ ไม่ว่าจะด้วยวิธีใดเหมาะสมoperator<<สำหรับหุ่นยนต์เพื่อให้แน่ใจว่าสถานะของกระแสข้อมูลมีการเปลี่ยนแปลงในบางวิธี ทั้งสองรูปแบบไม่มีการตั้งค่าสถานะยามใด ๆ มันเป็นเพียงพฤติกรรมของการดำเนินการแทรกที่มีการจัดรูปแบบถัดไปซึ่งจะกำหนดว่าส่วนใดของสถานะจะถูกรีเซ็ตหากมี
CB Bailey

3
แน่นอน! และเหตุผลเดียวที่setwดูเหมือนจะทำงานแตกต่างกันก็คือเนื่องจากมีข้อกำหนดเกี่ยวกับการดำเนินการเอาต์พุตที่จัดรูปแบบแล้วเพื่อ.width(0)สตรีมเอาต์พุตอย่างชัดเจน
CB Bailey

31

เหตุผลที่widthดูเหมือนจะไม่ 'เหนียว' คือการดำเนินการบางอย่างได้รับการรับรองให้เรียก.width(0)ใช้กระแสข้อมูลขาออก นั่นคือ:

21.3.7.9 [lib.string.io]:

template<class charT, class traits, class Allocator>
  basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>& os,
               const basic_string<charT,traits,Allocator>& str);

22.2.2.2.2 [lib.facet.num.put.virtuals]: do_putโอเวอร์โหลดทั้งหมดสำหรับnum_putเทมเพลต สิ่งเหล่านี้ถูกใช้โดยการโอเวอร์โหลดของoperator<<การถ่าย a basic_ostreamและแบบบิวด์อิน

22.2.6.2.2 [lib.locale.money.put.virtuals]: do_putโอเวอร์โหลดทั้งหมดสำหรับmoney_putเทมเพลต

27.6.2.5.4 [lib.ostream.inserters.character]: Overloads ของoperator<<การbasic_ostreamและเป็นหนึ่งในประเภทถ่านของ instantiation basic_ostream หรือcharลงนามcharหรือunsigned charหรือตัวชี้ไปยังอาร์เรย์ชนิดถ่านเหล่านี้

ความจริงแล้วฉันไม่แน่ใจเกี่ยวกับเหตุผลนี้ แต่ไม่ostreamควรรีเซ็ตสถานะอื่นของฟังก์ชันเอาต์พุตที่จัดรูปแบบแล้ว แน่นอนสิ่งที่ชอบbadbitและfailbitอาจถูกตั้งค่าหากมีความล้มเหลวในการดำเนินการส่งออก แต่ที่ควรจะคาดหวัง

เหตุผลเดียวที่ฉันคิดได้สำหรับการรีเซ็ตความกว้างคืออาจเป็นเรื่องที่น่าแปลกใจถ้าเมื่อพยายามที่จะเอาท์พุทเขตข้อมูลที่มีตัวคั่นบางตัวคั่นของคุณจะถูกทับ

เช่น

std::cout << std::setw(6) << 4.5 << '|' << 3.6 << '\n';

"   4.5     |   3.6      \n"

หากต้องการ 'แก้ไข' สิ่งนี้จะใช้เวลา:

std::cout << std::setw(6) << 4.5 << std::setw(0) << '|' << std::setw(6) << 3.6 << std::setw(0) << '\n';

ในขณะที่มีความกว้างการรีเซ็ตผลลัพธ์ที่ต้องการสามารถสร้างขึ้นด้วยความสั้น:

std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n';

6

setw()ส่งผลต่อการแทรกครั้งถัดไปเท่านั้น นั่นเป็นเพียงวิธีการsetw()ทำงาน ลักษณะการทำงานของเป็นเช่นเดียวกับsetw() ios_base::width()ผมได้รับของฉันsetw()ข้อมูลจากcplusplus.com

คุณสามารถค้นหารายชื่อเต็มของ manipulators ที่นี่ จากลิงก์นั้นธงสตรีมทั้งหมดควรบอกว่าตั้งค่าจนกว่าจะมีการเปลี่ยนแปลงโดยผู้ควบคุมรายอื่น หนึ่งทราบเกี่ยวกับleft, rightและinternalmanipulators: พวกเขาเป็นเหมือนธงอื่น ๆ และจะยังคงมีอยู่จนกว่าจะมีการเปลี่ยนแปลง อย่างไรก็ตามจะมีผลต่อเมื่อตั้งค่าความกว้างของสตรีมและต้องตั้งค่าความกว้างทุกบรรทัด ตัวอย่างเช่น

cout.width(6);
cout << right << "a" << endl;
cout.width(6);
cout << "b" << endl;
cout.width(6);
cout << "c" << endl;

จะให้คุณ

>     a
>     b
>     c

แต่

cout.width(6);
cout << right << "a" << endl;
cout << "b" << endl;
cout << "c" << endl;

จะให้คุณ

>     a
>b
>c

เครื่องมือจัดการอินพุตและเอาต์พุตไม่เหนียวเหนอะและจะเกิดขึ้นเพียงครั้งเดียวเมื่อใช้งาน ผู้ควบคุม parametrized มีความแตกต่างกันนี่คือคำอธิบายสั้น ๆ ของแต่ละคน:

setiosflagsช่วยให้คุณตั้งค่าสถานะด้วยตนเองรายการที่สามารถ fount ได้ที่นี่จึงเหนียว

resetiosflagsทำงานคล้ายกันsetiosflagsยกเว้นว่าไม่ได้ตั้งค่าสถานะที่ระบุ

setbase ตั้งค่าฐานของจำนวนเต็มที่แทรกเข้าไปในสตรีม (ดังนั้น 17 ในฐาน 16 จะเป็น "11" และในฐาน 2 จะเป็น "1,0001")

setfillตั้งค่าอักขระเติมเพื่อแทรกในสตรีมเมื่อsetwใช้

setprecision ตั้งค่าความแม่นยำทศนิยมที่จะใช้เมื่อแทรกค่าทศนิยม

setw ทำให้การแทรกความกว้างที่ระบุถัดไปโดยการกรอกด้วยอักขระที่ระบุใน setfill


ส่วนใหญ่เป็นเพียงการตั้งค่าสถานะดังนั้นสิ่งเหล่านั้น "เหนียว" setw () ดูเหมือนจะเป็นคนเดียวที่มีผลต่อการแทรกเพียงครั้งเดียว คุณสามารถหารายละเอียดเพิ่มเติมสำหรับแต่ละที่cplusplus.com/reference/iostream/manipulators
เดวิดบราวน์

ดีstd::hexยังไม่เหนียวเหนอะหนะและเห็นได้ชัดstd::flushหรือstd::setiosflagsไม่ได้เหนียวอย่างใดอย่างหนึ่ง ดังนั้นฉันไม่คิดว่ามันง่ายขนาดนั้น
sbi

เพียงทดสอบ hex และ setiosflags () ทั้งคู่ดูเหมือนจะเหนียว (ทั้งคู่ตั้งค่าสถานะที่คงอยู่สำหรับสตรีมนั้นจนกว่าคุณจะเปลี่ยน)
David Brown

ใช่หน้าเว็บที่อ้างว่าstd::hexไม่เหนียวนั้นผิด - ฉันเพิ่งค้นพบสิ่งนี้เช่นกัน อย่างไรก็ตามการตั้งค่าสถานะสตรีมอาจมีการเปลี่ยนแปลงแม้ว่าคุณจะไม่ได้ใส่std::setiosflagsอีกครั้งดังนั้นใคร ๆ ก็สามารถดูได้ว่าไม่เหนียวเหนอะ อีกทั้งstd::wsไม่เหนียวเหนอะ ดังนั้นมันจึงเป็นไม่ได้ที่ง่าย
sbi

คุณใช้ความพยายามค่อนข้างมากในการปรับปรุงคำตอบของคุณ +1
sbi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.