2009-10-07 25 views
117

Gần đây tôi đã gặp sự cố khi tạo chuỗi chuỗi do thực tế rằng tôi đã giả định không đúng std :: setw() sẽ ảnh hưởng đến chuỗi cho mỗi lần chèn, cho đến khi tôi thay đổi nó một cách rõ ràng. Tuy nhiên, nó luôn luôn không được đặt sau khi chèn.Những người thao túng iomanip nào 'dính'?

// 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' 

Vì vậy, tôi có một số câu hỏi:

  • Tại sao setw() theo cách này?
  • Có người thao túng nào khác theo cách này không?
  • Có sự khác biệt về hành vi giữa std :: ios_base :: width() và std :: setw() không?
  • Cuối cùng có tài liệu tham khảo trực tuyến ghi rõ hành vi này? Tài liệu nhà cung cấp của tôi (MS Visual Studio 2005) dường như không hiển thị rõ ràng điều này.
+0

Một vòng làm việc là ở đây: http://stackoverflow.com/a/37495361/984471 –

Trả lời

78

Ghi chú quan trọng từ các ý kiến ​​dưới đây:

By Martin:

@Chareles: Sau đó, bằng yêu cầu này tất cả các thao tác đều dính. Ngoại trừ setw mà dường như được thiết lập lại sau khi sử dụng.

By Charles:

Chính xác! và lý do duy nhất mà setw xuất hiện để hoạt động khác nhau là vì có các yêu cầu về hoạt động đầu ra được định dạng rõ ràng .width (0) luồng đầu ra.

Sau đây là các cuộc thảo luận mà dẫn đến kết luận trên:


Nhìn vào mã thao tác sau đây trả về một đối tượng chứ không phải là một dòng:

setiosflags 
resetiosflags 
setbase 
setfill 
setprecision 
setw 

Đây là một kỹ thuật chung để áp dụng thao tác cho đối tượng tiếp theo được áp dụng cho luồng. Thật không may điều này không loại trừ chúng khỏi bị dính. Các thử nghiệm chỉ ra rằng tất cả chúng ngoại trừ setw đều dính.

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

Tất cả các thao tác khác trả về đối tượng luồng. Vì vậy, bất kỳ thông tin trạng thái nào mà chúng thay đổi phải được ghi lại trong đối tượng luồng và do đó là vĩnh viễn (cho đến khi một trình điều khiển khác thay đổi trạng thái). Vì vậy, những người thao túng sau đây phải là Chú ý thao tác.

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

dec/ hex/ oct 

fixed/ scientific 

internal/ left/ right 

Những người thao tác này thực sự thực hiện một hoạt động trên luồng chứ không phải đối tượng luồng (Mặc dù luồng kỹ thuật là một phần của trạng thái đối tượng dòng). Nhưng tôi không tin rằng chúng ảnh hưởng đến bất kỳ phần nào khác của trạng thái đối tượng luồng.

ws/ endl/ ends/ flush 

Kết luận là setw dường như là người thao túng duy nhất trên phiên bản của tôi không bị dính.

Đối với Charles một thủ thuật đơn giản để chỉ ảnh hưởng đến mục tiếp theo trong chuỗi:
Dưới đây là một ví dụ làm thế nào một đối tượng có thể được sử dụng để temporaily thay đổi trạng thái sau đó đặt nó trở lại bằng cách sử dụng một đối tượng:

#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 
+0

Cheat sheet đẹp. Thêm một tham chiếu đến nơi thông tin đến từ đó, và nó sẽ là một câu trả lời hoàn hảo. –

+1

Tôi đã xem tệp "iomanip" trong thư mục bao gồm gcc. –

+1

Tuy nhiên tôi có thể xác minh rằng setfill() là trong thực tế 'dính' mặc dù nó trả về một đối tượng. Vì vậy, tôi nghĩ câu trả lời này không đúng. –

5

setw() chỉ ảnh hưởng đến lần chèn tiếp theo. Đó chỉ là cách hành xử setw(). Hành vi của setw() cũng giống như ios_base::width(). Tôi đã nhận được thông tin setw() từ cplusplus.com.

Bạn có thể tìm danh sách đầy đủ các thao tác here. Từ liên kết đó, tất cả các cờ luồng sẽ được đặt cho đến khi được thay đổi bởi một trình điều khiển khác. Một lưu ý về các trình điều khiển left, rightinternal: Chúng giống như các cờ khác và làm vẫn tồn tại cho đến khi thay đổi. Tuy nhiên, chúng chỉ có hiệu lực khi chiều rộng của luồng được thiết lập và chiều rộng phải được đặt ở mọi dòng. Vì vậy, ví dụ

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

sẽ cung cấp cho bạn

>  a 
>  b 
>  c 

nhưng

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

sẽ cung cấp cho bạn

>  a 
>b 
>c 

Các thao tác Input và Output là không dính và chỉ xảy ra một khi chúng được sử dụng. Các trình điều khiển được parametrized khác nhau, dưới đây là mô tả ngắn gọn về mỗi:

setiosflags cho phép bạn đặt cờ theo cách thủ công, danh sách có thể là số here.

resetiosflags hoạt động tương tự như setiosflags ngoại trừ việc hủy bỏ cờ được chỉ định.

setbase đặt cơ sở các số nguyên được chèn vào luồng (do đó 17 trong số 16 sẽ là "11" và trong cơ sở 2 sẽ là "10001").

setfill đặt ký tự điền để chèn vào luồng khi sử dụng setw.

setprecision đặt độ chính xác thập phân sẽ được sử dụng khi chèn các giá trị dấu phẩy động.

setw làm chỉ chèn tiếp theo chiều rộng quy định bằng cách điền vào với nhân vật quy định tại setfill

+3

"Những người thao túng iomanip nào 'dính'?" – sbi

+0

Vâng, hầu hết trong số họ chỉ là thiết lập cờ, vì vậy đó là những "dính". setw() có vẻ là người duy nhất ảnh hưởng đến chỉ một lần chèn. Bạn có thể tìm hiểu thêm chi tiết cụ thể cho từng địa chỉ tại http://www.cplusplus.com/reference/iostream/manipulators/ –

+0

Vâng 'std :: hex' cũng không bị dính và rõ ràng là' std :: flush' hoặc ' std :: setiosflags' cũng không dính. Vì vậy, tôi không nghĩ nó đơn giản như vậy. – sbi

29

Lý do mà width không có vẻ là 'dính' là các hoạt động nhất định được đảm bảo để gọi .width(0) trên luồng đầu ra. Đó là:

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]: Tất cả do_put quá tải cho num_put mẫu.Chúng được sử dụng bởi quá tải operator<< chụp basic_ostream và được xây dựng bằng kiểu số.

22.2.6.2.2 [lib.locale.money.put.virtuals]: Tất cả quá tải do_put cho mẫu money_put.

27.6.2.5.4 [lib.ostream.inserters.character]: quá tải của operator<< tham gia một basic_ostream và một trong những kiểu char của instantiation basic_ostream hoặc charchar hoặc unsigned char hoặc con trỏ đến mảng của các loại char .

Thành thật mà nói, tôi không chắc chắn về lý do cho điều này, nhưng không có trạng thái nào khác của ostream phải được đặt lại bằng chức năng đầu ra được định dạng. Tất nhiên, những thứ như badbitfailbit có thể được đặt nếu có lỗi trong hoạt động đầu ra, nhưng điều đó sẽ được mong đợi.

Lý do duy nhất tôi có thể nghĩ đến để đặt lại chiều rộng là có thể ngạc nhiên nếu khi cố gắng xuất một số trường được phân cách, dấu phân cách của bạn đã được đệm.

Ví dụ:

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

" 4.5  | 3.6  \n" 

Để 'đúng' này sẽ thực hiện:

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

trong khi với chiều rộng đặt lại, các đầu ra mong muốn có thể được tạo ra với sự ngắn:

std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n'; 
Các vấn đề liên quan