Как понять непоследовательное поведение С++ 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-й попытки, вы заметите, что «строковый» вывод строки заголовка смещен от «числового» вывода строк данных... и хотя он «в основном» выполняет цель выравнивания вещей в столбцах , это и не точно, и не последовательно.

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

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

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

4545.323         45.5468        
...       
6363.323         63.5468      

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

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

... повлиять на "будущие" выполнения setw()?

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

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


person George 2.0 Hope    schedule 13.04.2018    source источник
comment
Это полностью соответствует. В первой попытке строка заголовков начинается с 4 символов, за которыми следуют 15 символов (в основном пробелы), а затем 15 символов (опять же в основном пробелы). Следующая строка состоит из 2 символов, затем 15 символов, а затем 15 символов. Чтобы выровнять вещи, вы также должны установить ширину первого столбца.   -  person Pete Becker    schedule 14.04.2018
comment
что вы имеете в виду под непоследовательным? Ints - это 4 символа, а 45 - 2, поэтому 1-й не выровнен.   -  person 463035818_is_not_a_number    schedule 14.04.2018
comment
@PeteBecker ~ хорошо, теперь я понимаю, почему вы говорите, что 1-я попытка непротиворечива ... по умолчанию setw() должен эффективно leftPad/выравнивать столбец по правому краю по умолчанию (а не так, как это было объяснено в учебнике, которому я следовал) ... это сказал, что он по-прежнему не устраняет странности, которые представляют собой 1-ю и 3-ю строки данных второй попытки.?.   -  person George 2.0 Hope    schedule 14.04.2018
comment
@George2.0Hope Вы должны установить ширину вывода first, это относится и к первому столбцу.   -  person Killzone Kid    schedule 14.04.2018
comment
@KillzoneKid с первой попытки, как я понял из моего ответа @PeteBecker; однако, даже если я изменю вторую попытку, она все равно не исправит 1-ю и 3-ю строки данных.   -  person George 2.0 Hope    schedule 14.04.2018
comment
@George2.0Надеюсь, я не понимаю, о чем вы говорите   -  person Killzone Kid    schedule 14.04.2018
comment
@KillzoneKid, если вы посмотрите на: ideone.com/yhH7l5 ||или|| repl.it/@Tredekka/Cpp-Understanding-stdsetw-1 (25 и 34) и (27 и 36) идентичны... однако их вывод не совпадает, почему?   -  person George 2.0 Hope    schedule 14.04.2018
comment
В отличие от setw, left является постоянным: достаточно вызвать его один раз, и он запомнится.   -  person Daniel H    schedule 14.04.2018


Ответы (1)


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

Вам нужно понимать диапазон, который имеет каждое из этих выражений. В частности, std::setfill(int) и std::left/std::right изменяют поведение по умолчанию и кажутся последними, пока не будут перезаписаны другим 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, что дополнительно очищает буфер после каждой строки.)

person Patrick vD    schedule 23.03.2021