Как понять противоречивое поведение C++ std::setw?

Учитывая следующий код:

/*Formatting Output 
**Goal: practice using cout to format output to console
**Print the variables in three columns:
**Ints, Floats, Doubles
*/

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    int a = 45;
    float b = 45.323;
    double c = 45.5468;
    int aa = a + 9;
    float bb = b + 9;
    double cc = c + 9;
    int aaa = aa + 9;
    float bbb = bb + 9;
    double ccc = cc + 9;

    // 1st attempt :>

    cout << "\n\n\n" << "// 1st attempt :>" << "\n";
    cout << "12345678901234567890123456789012345678901234567890" << "\n";
    cout << "Ints" << setw(15) << "Floats" << setw(15) << "Doubles" << "\n";
    cout << a << setw(15) << b << setw(15) << c << "\n";
    cout << aa << setw(15) << bb << setw(15) << cc << "\n";
    cout << aaa << setw(15) << bbb << setw(15) << ccc << "\n";


    // 2nd attempt :>

    cout << "\n\n\n" << "// 2nd attempt :>" << "\n";
    cout << "12345678901234567890123456789012345678901234567890" << "\n";
    cout << std::left << std::setfill(' ') << std::setw(15)  << "Ints" << setw(15) << "Floats" << setw(15) << "Doubles" << "\n";
    cout << a << setw(15) << b << setw(15) << c << "\n";
    cout << std::left << std::setfill(' ') << setw(15) << aa << setw(15) << bb << setw(15) << cc << "\n";
    cout << aaa << setw(15) << bbb << setw(15) << ccc << "\n";

    // 3rd attempt :>

    cout << "\n\n\n" << "// 3rd attempt :>" << "\n";
    cout << "12345678901234567890123456789012345678901234567890" << "\n";
    cout << std::left << std::setfill(' ') << std::setw(15) << "Ints" << setw(15) << "Floats" << setw(15) << "Doubles" << "\n";
    cout << std::left << std::setfill(' ') << std::setw(15) << a << setw(15) << b << setw(15) << c << "\n";
    cout << std::left << std::setfill(' ') << std::setw(15) << aa << setw(15) << bb << setw(15) << cc << "\n";
    cout << std::left << std::setfill(' ') << std::setw(15) << aaa << setw(15) << bbb << setw(15) << ccc << "\n";
    cout << "12345678901234567890123456789012345678901234567890" << "\n";
    cout << std::right << std::setfill(' ') << std::setw(15) << "Ints" << setw(15) << "Floats" << setw(15) << "Doubles" << "\n";
    cout << std::right << std::setfill(' ') << std::setw(15) << a << setw(15) << b << setw(15) << c << "\n";
    cout << std::right << std::setfill(' ') << std::setw(15) << aa << setw(15) << bb << setw(15) << cc << "\n";
    cout << std::right << std::setfill(' ') << std::setw(15) << aaa << setw(15) << bbb << setw(15) << ccc << "\n";

    return 0;
}
// https://repl.it/@Tredekka/Cpp-Understanding-stdsetw

... я получаю следующий вывод:

gcc version 4.6.3

// 1st attempt :>
12345678901234567890123456789012345678901234567890
Ints         Floats        Doubles
45         45.323        45.5468
54         54.323        54.5468
63         63.323        63.5468



// 2nd attempt :>
12345678901234567890123456789012345678901234567890
Ints           Floats         Doubles        
4545.323         45.5468        
54             54.323         54.5468        
6363.323         63.5468        



// 3rd attempt :>
12345678901234567890123456789012345678901234567890
Ints           Floats         Doubles        
45             45.323         45.5468        
54             54.323         54.5468        
63             63.323         63.5468        
12345678901234567890123456789012345678901234567890
           Ints         Floats        Doubles
             45         45.323        45.5468
             54         54.323        54.5468
             63         63.323        63.5468

... примечание: я намеренно был "несовместим" с кодом, "потому что" я пытаюсь понять поведение <iomanip> && std::setw() код.

Если вы посмотрите на вывод с 1-й попытки, то заметите, что вывод "строки" строки заголовка смещен от "числового" вывода строк данных... и хотя он "в основном" выполняет задачу выравнивания элементов в столбцах Это не точно и не соответствует.

Во 2-й попытке вы увидите, что я обнаружил, что если я добавлю вывод строки с помощью:

<< std::left << std::setfill(' ') << setw(15)

... тогда я получаю строку, которая выглядит правильно (как видно из заголовка и второй строки данных)... но теперь вы заметите, что первая и третья строки данных очень неправильны:

4545.323         45.5468        
...       
6363.323         63.5468      

... как происходит "использование / выполнение"...

<< std::left << std::setfill(' ') << setw(15)

... влияет на "будущие" казни setw() ?

Ради полноты я показал в 3-й попытке, что можно правильно и точно выровнять столбцы данных (влево или вправо), используя <iomanip> && std::setw()... но почему несоответствия?

(Ответ @WhozCraig помог мне добраться до того места, где я нахожусь, но не углубился достаточно глубоко, чтобы помочь мне понять также: (a) почему это "псевдо" работает || (b) почему после того, как вы заставили его работать правильно "в первый раз" затем он нарушает функциональность "псевдо".)

2 ответа

Причиной того, что вы говорите, является "непоследовательность", является тот факт, что вы не знаете, как std::setw(), Он не печатает первый символ каждые 15 позиций, скорее он печатает 15 пробелов после последнего символа. Так чтобы получить consistant Значение, которое вы должны выровнять по правому краю в первом ряду.

cout << setw(4) << "Ints" << setw(15) << "Floats" << setw(15) << "Doubles" << "\n";
cout << setw(4) << a << setw(15) << b << setw(15) << c << "\n";
cout << setw(4) << aa << setw(15) << bb << setw(15) << cc << "\n";
cout << setw(4) << aaa << setw(15) << bbb << setw(15) << ccc << "\n";

Это даст следующий результат:

Ints         Floats        Doubles
  45         45.323        45.5468 // in this line setw() counts 15 spaces from number 5.
  54         54.323        54.5468
  63         63.323        63.5468

Вот ваша проблема: std::setw() не действует как буфер, он изменяет способ вычисления следующего выражения.

Вам необходимо понимать «диапазон», который имеет каждое из этих выражений. Конкретно, std::setfill(int) и / изменить поведение по умолчанию и казаться последним, пока они не будут перезаписаны другим setfill() или же std::left/ std::right. std::setw(int) с другой стороны, похоже, влияет только на то, что передается сразу после него (что странно, потому что я чувствую, что также видел, как он ведет себя как std::setfill и другие раньше)

Итак, чтобы подвести итог, вы хотите чего-то более похожего на это:

          int a = 45;
    float b = 45.323;
    double c = 45.5468;
    int aa = a + 9;
    float bb = b + 9;
    double cc = c + 9;
    int aaa = aa + 9;
    float bbb = bb + 9;
    double ccc = cc + 9;


    std::cout << std::endl << std::endl << std::endl;

    std::cout << std::left << std::setfill('~');

    // 1st attempt :>
    std::cout << "// 1st attempt :>" << std::endl;
    std::cout << "12345678901234567890123456789012345678901234567890" << std::endl;
    std::cout << std::setw(10) << "Ints" << std::setw(10) << "Floats" << std::setw(10) << "Doubles" << std::endl;
    std::cout << std::setw(10) << a   << std::setw(10) << b   << std::setw(10) << c   << std::endl;
    std::cout << std::setw(10) << aa  << std::setw(10) << bb  << std::setw(10) << cc  << std::endl;
    std::cout << std::setw(10) << aaa << std::setw(10) << bbb << std::setw(10) << ccc << std::endl;


    std::cout << std::endl << std::endl << std::endl << std::setfill('*');


    // 2nd attempt :>
    std::cout << "// 2nd attempt :>" << std::endl;
    std::cout << "12345678901234567890123456789012345678901234567890" << std::endl;
    std::cout << std::setw(10) << "Ints" << std::setw(10) << "Floats" << std::setw(10) << "Doubles" << std::endl;
    std::cout << std::setw(10) << a   << std::setw(10) << b   << std::setw(10) << c   << std::endl;
    std::cout << std::setw(10) << aa  << std::setw(10) << bb  << std::setw(10) << cc  << std::endl;
    std::cout << std::setw(10) << aaa << std::setw(10) << bbb << std::setw(10) << ccc << std::endl;


    std::cout << std::endl << std::endl << std::endl;


    // 3rd attempt :>
    std::cout << "// 3rd attempt :>" << std::endl;
    std::cout << "12345678901234567890123456789012345678901234567890" << std::endl;
    std::cout << std::setw(10) << "Ints" << std::setw(10) << "Floats" << std::setw(10) << "Doubles" << std::endl;
    std::cout << std::setw(10) << a   << std::setw(10) << b   << std::setw(10) << c   << std::endl;
    std::cout << std::setw(10) << aa  << std::setw(10) << bb  << std::setw(10) << cc  << std::endl;
    std::cout << std::setw(10) << aaa << std::setw(10) << bbb << std::setw(10) << ccc << std::endl;

    std::cout << "12345678901234567890123456789012345678901234567890" << std::endl;
    std::cout << std::right << std::setfill(' ');
    std::cout << std::setw(10) << "Ints" << std::setw(10) << "Floats" << std::setw(10) << "Doubles" << std::endl;
    std::cout << std::setw(10) << a   << std::setw(10) << b   << std::setw(10) << c   << std::endl;
    std::cout << std::setw(10) << aa  << std::setw(10) << bb  << std::setw(10) << cc  << std::endl;
    std::cout << std::setw(10) << aaa << std::setw(10) << bbb << std::setw(10) << ccc << std::endl;

    std::cout << std::endl << std::endl << std::endl;

(Вы заметите, что я тоже изменился "\n" к std::endl который дополнительно очищает буфер после каждой строки.)

Другие вопросы по тегам