2009-09-08 33 views
15

Có cách nào dễ dàng thụt lề đầu ra đi đến một đối tượng của dòng không? Tôi có một mảng ký tự C++ là null chấm dứt và bao gồm các dòng mới. Tôi muốn xuất kết quả này vào luồng nhưng thụt lề mỗi dòng với hai dấu cách. Có cách nào dễ dàng để thực hiện điều này với các trình xử lý luồng như bạn có thể thay đổi cơ sở cho đầu ra số nguyên với các chỉ thị đặc biệt cho luồng hay tôi phải tự xử lý mảng và chèn thêm dấu cách theo cách thủ công tại mỗi ngắt dòng được phát hiện không?Làm cách nào để dễ dàng thụt lề đầu ra cho luồng?

Có vẻ như chuỗi :: đúng() thao túng gần:

http://www.cplusplus.com/reference/iostream/manipulators/right/

Cảm ơn.

-William

+0

có lẽ đã đến lúc ai đó viết thư viện cho điều này :) – xtofl

+1

Đã có sẵn. Nó được gọi là một khía cạnh. Nó được sử dụng để định dạng đầu ra luồng. Do đó, người dùng luồng chỉ có thể xuất dữ liệu như bình thường. Các khía cạnh sau đó có thể thực hiện bất kỳ định dạng độc lập (do đó định dạng đầu ra có thể được thay đổi chỉ bằng cách thay đổi khía cạnh mà dòng sử dụng mà không thay đổi mã tạo ra đầu ra). –

Trả lời

21

Đây là tình huống hoàn hảo để sử dụng một khía cạnh.

Phiên bản tùy chỉnh của khía cạnh codecvt có thể được nhúng vào luồng.

Vì vậy, việc sử dụng của bạn sẽ trông như thế này:

int main() 
{ 
    /* Imbue std::cout before it is used */ 
    std::ios::sync_with_stdio(false); 
    std::cout.imbue(std::locale(std::locale::classic(), new IndentFacet())); 

    std::cout << "Line 1\nLine 2\nLine 3\n"; 

    /* You must imbue a file stream before it is opened. */ 
    std::ofstream  data; 
    data.imbue(indentLocale); 
    data.open("PLOP"); 

    data << "Loki\nUses Locale\nTo do something silly\n"; 
} 

Định nghĩa của các khía cạnh là hơi phức tạp.
Nhưng toàn bộ vấn đề là ai đó sử dụng khía cạnh không cần biết gì về định dạng. Định dạng được áp dụng độc lập với cách sử dụng luồng.

#include <locale> 
#include <algorithm> 
#include <iostream> 
#include <fstream> 

class IndentFacet: public std::codecvt<char,char,std::mbstate_t> 
{ 
    public: 
    explicit IndentFacet(size_t ref = 0): std::codecvt<char,char,std::mbstate_t>(ref) {} 

    typedef std::codecvt_base::result    result; 
    typedef std::codecvt<char,char,std::mbstate_t> parent; 
    typedef parent::intern_type      intern_type; 
    typedef parent::extern_type      extern_type; 
    typedef parent::state_type      state_type; 

    int& state(state_type& s) const   {return *reinterpret_cast<int*>(&s);} 
    protected: 
    virtual result do_out(state_type& tabNeeded, 
         const intern_type* rStart, const intern_type* rEnd, const intern_type*& rNewStart, 
         extern_type*  wStart, extern_type*  wEnd, extern_type*&   wNewStart) const 
    { 
     result res = std::codecvt_base::noconv; 

     for(;(rStart < rEnd) && (wStart < wEnd);++rStart,++wStart) 
     { 
      // 0 indicates that the last character seen was a newline. 
      // thus we will print a tab before it. Ignore it the next 
      // character is also a newline 
      if ((state(tabNeeded) == 0) && (*rStart != '\n')) 
      { 
       res     = std::codecvt_base::ok; 
       state(tabNeeded) = 1; 
       *wStart    = '\t'; 
       ++wStart; 
       if (wStart == wEnd) 
       { 
        res  = std::codecvt_base::partial; 
        break; 
       } 
      } 
      // Copy the next character. 
      *wStart   = *rStart; 

      // If the character copied was a '\n' mark that state 
      if (*rStart == '\n') 
      { 
       state(tabNeeded) = 0; 
      } 
     } 

     if (rStart != rEnd) 
     { 
      res = std::codecvt_base::partial; 
     } 
     rNewStart = rStart; 
     wNewStart = wStart; 

     return res; 
    } 

    // Override so the do_out() virtual function is called. 
    virtual bool do_always_noconv() const throw() 
    { 
     return false; // Sometime we add extra tabs 
    } 

}; 

Xem: Tom's notes below

+0

Kết quả mong đợi ở đây là gì? Dường như không hoạt động như được quảng cáo ở đây: [http://liveworkspace.org/code/T4tCi$0](http://liveworkspace.org/code/T4tCi$0) – sehe

+0

@sehe: std :: cout thật buồn cười. Hàm imbue() sẽ không hoạt động trên bất kỳ luồng nào nếu luồng đó vẫn được sử dụng. Một số triển khai sử dụng std :: cout trước main() do đó imbue có thể thất bại() trong đoạn mã trên. Nhưng nó sẽ luôn hoạt động trên tập tin. Vì vậy, hãy kiểm tra nội dung của tệp PLOP. –

+0

Tôi nghĩ rằng tôi thấy nó không làm việc với ostringstream quá. Tôi sẽ cố gắng kiểm tra sau – sehe

2

Vâng đây không phải là câu trả lời tôi đang tìm kiếm, nhưng trong trường hợp không có câu trả lời như vậy, đây là một cách để làm điều này bằng tay:

void 
indentedOutput(ostream &outStream, const char *message, bool &newline) 
{ 
    while (char cur = *message) { 
    if (newline) { 
     outStream << " "; 
     newline = false; 
    } 
    outStream << cur; 
    if (cur == '\n') { 
     newline = true; 
    } 
    ++message; 
    } 
} 
1

Không có cách đơn giản, nhưng rất nhiều đã được viết về phức tạp cách để đạt được điều này. Read this article để có giải thích tốt về chủ đề. Here is another article, thật không may bằng tiếng Đức. Nhưng its source code sẽ giúp bạn.

Ví dụ: bạn có thể viết một hàm ghi nhật ký cấu trúc đệ quy. Đối với mỗi cấp độ của đệ quy thụt đầu dòng được tăng lên:

std::ostream& operator<<(std::ostream& stream, Parameter* rp) 
{ 
    stream << "Parameter: " << std::endl; 

    // Get current indent 
    int w = format::get_indent(stream); 

    stream << "Name: " << rp->getName(); 
    // ... log other attributes as well 

    if (rp->hasParameters()) 
    { 
     stream << "subparameter (" << rp->getNumParameters() << "):\n"; 

     // Change indent for sub-levels in the hierarchy 
     stream << format::indent(w+4); 

     // write sub parameters   
     stream << rp->getParameters(); 
    } 

    // Now reset indent 
    stream << format::indent(w); 

    return stream; 

} 
2

Một cách để thêm tính năng như vậy sẽ phải viết một streambuf lọc (tức là một streambuf mà sẽ chuyển hoạt động IO để streambuf khác nhưng thao tác dữ liệu chuyển giao) mà thêm thụt đầu dòng như là một phần của hoạt động lọc của nó. Tôi đã đưa ra một ví dụ về việc viết một streambuf here và tăng cường cung cấp một library để trợ giúp trong đó.

Nếu trường hợp của bạn, thành viên tràn() chỉ đơn giản là sẽ thử nghiệm cho '\ n' và sau đó thêm thụt lề ngay sau nếu cần thiết (chính xác những gì bạn đã làm trong chức năng indentedOuput của bạn, ngoại trừ rằng newline sẽ là một thành viên của streambuf). Bạn có thể có thể có một thiết lập để tăng hoặc giảm kích thước thụt lề (có thể truy cập thông qua một trình điều khiển, trình điều khiển sẽ phải thực hiện một dynamic_cast để đảm bảo rằng streambuf liên quan đến luồng là đúng loại; có một cơ chế để thêm người dùng dữ liệu để truyền - basic_ios :: xalloc, iword và pword - nhưng ở đây chúng tôi muốn hành động trên streambuf).

2

Tôi đã thành công tốt đẹp với Martin codecvt khía cạnh dựa gợi ý, nhưng tôi đã vấn đề sử dụng nó trên std :: cout trên OSX, vì theo mặc định dòng này sử dụng một stream_uf dựa trên basic_streambuf bỏ qua khía cạnh được thấm nhuần. Dòng sau chuyển std :: cout và bạn bè để sử dụng streambuf dựa trên basic_filebuf, sẽ sử dụng khía cạnh được thấm nhuần.

std::ios::sync_with_stdio(false); 

Với hiệu ứng phụ liên quan, các đối tượng dòng tiêu chuẩn iostream có thể hoạt động độc lập với luồng C chuẩn.

Một lưu ý khác là do khía cạnh này không có static std :: locale :: id, có nghĩa là gọi std :: has_facet <IndentFacet> trên ngôn ngữ luôn trả về true. Thêm một std :: local :: id có nghĩa là facet không được sử dụng, vì basic_filebuf tìm kiếm mẫu lớp cơ sở.

+0

Cảm ơn bạn. Tôi đã tìm kiếm một giải pháp cho điều này cho lứa tuổi. –

0

Simple thao túng khoảng trắng

struct Whitespace 
{ 
    Whitespace(int n) 
     : n(n) 
    { 
    } 
    int n; 
}; 

std::ostream& operator<<(std::ostream& stream, const Whitespace &ws) 
{ 
    for(int i = 0; i < ws.n; i++) 
    { 
     stream << " "; 
    } 
    return stream; 
} 
1

tôi đã khái quát hóa giải pháp Loki Astarti để làm việc với mức thụt đầu dòng tùy ý. Giải pháp có giao diện đẹp, dễ sử dụng, nhưng việc triển khai thực tế lại hơi khó khăn. Nó có thể được tìm thấy trên github: https://github.com/spacemoose/ostream_indenter

Có một bản demo tham gia nhiều hơn trong repo github, nhưng đưa ra:

#include "indent_facet.hpp" 

/// This probably has to be called once for every program: 
// http://stackoverflow.com/questions/26387054/how-can-i-use-stdimbue-to-set-the-locale-for-stdwcout 
std::ios_base::sync_with_stdio(false); 

// This is the demo code: 
std::cout << "I want to push indentation levels:\n" << indent_manip::push 
      << "To arbitrary depths\n" << indent_manip::push 
      << "and pop them\n" << indent_manip::pop 
      << "back down\n" << indent_manip::pop 
      << "like this.\n" << indent_manip::pop; 

}

Nó tạo ra kết quả như sau:

I want to push indentation levels: 
    To arbitrary depths 
     and pop them 
    back down 
like this. 

Tôi sẽ đánh giá cao bất kỳ phản hồi nào về tiện ích của mã.

+0

trong khi đây thực sự là một ý tưởng tuyệt vời về việc sử dụng codecvt để tự động hóa, có một vài vấn đề với mã nguồn của bạn: 1) Khi in các tab trong vòng lặp, bạn không kiểm tra tràn bộ đệm '_to', điều này có thể được giải quyết dễ dàng; (để được tiếp tục trong phần tiếp theo) – segfault

+0

2) Sau khi khắc phục vấn đề đầu tiên, có một vấn đề khác với việc triển khai libstdC++ của codecvt, mà chỉ gọi 'do_out' một lần nếu kết quả của hoạt động cuối cùng là' std :: codecvt_base :: partial ', do đó, nếu dòng chỉ có vài ký tự (ví dụ: mở ngoặc nhọn) và mức thụt lề là lớn, một số ký tự sẽ bị mất. Tôi không biết cách giải quyết điều này một cách chính xác, giải pháp xấu xí duy nhất mà tôi biết là ghi đè 'do_max_length()' để trả về một giá trị lớn buộc libstdC++ cấp phát bộ đệm đầu ra đủ lớn. – segfault

Các vấn đề liên quan