С++ setw() не работает должным образом

Мне нужно распечатать некоторые данные в консоли. Мой код:

cout << setw(5) << left << "id" << " | " << setw(10) << left << "computer" << " | " << setw(11) << left << "subsystem" << " | " <<
        setw(8) << left << "number" << " | " << setw(80) << left << "name" << " | " << setw(13) << left << "config_file" << endl;
    for (int i = 0; i < rows; i++)
        {
        cout << setw(5) << left << subsystem_table_data[i].id << " | " << setw(10) << left << subsystem_table_data[i].computer << " | " <<
                setw(11) << left << subsystem_table_data[i].subsystem << " | " << setw(8) << left << subsystem_table_data[i].number << " | " <<
                setw(80) << left << subsystem_table_data[i].name << " | " << setw(13) << left << subsystem_table_data[i].config_file << endl;
        }

Вывод (прокрутите вправо, чтобы увидеть):

id    | computer   | subsystem   | number   | name                                                                             | config_file  
1     | 1          | 2           | 0        | Computer 1 - Общая компьютерная платформа - 1          | 1            
2     | 1          | 1           | 0        | Computer 1 - Launcher - 1                                                        | 2            
3     | 1          | 23          | 0        | Computer 1 - Дисплей - 1                                                  | 3            
4     | 1          | 11          | 0        | Computer 1 - Контроллер цифровой - 1                           | 4            
5     | 1          | 21          | 0        | Computer 1 - Отладки - 1                                                  | 5            
6     | 2          | 2           | 0        | Computer 2 - Общая компьютерная платформа - 1          | 6            
7     | 2          | 1           | 0        | Computer 2 - Launcher - 1                                                        | 7            
8     | 2          | 23          | 0        | Computer 2 - Дисплей - 1                                                  | 8          

Ожидаемый результат (снова справа):

id    | computer   | subsystem   | number   | name                                                                             | config_file  
1     | 1          | 2           | 0        | Computer 1 - Общая компьютерная платформа - 1                                    | 1            
2     | 1          | 1           | 0        | Computer 1 - Launcher - 1                                                        | 2            
3     | 1          | 23          | 0        | Computer 1 - Дисплей - 1                                                         | 3            
4     | 1          | 11          | 0        | Computer 1 - Контроллер цифровой - 1                                             | 4            
5     | 1          | 21          | 0        | Computer 1 - Отладки - 1                                                         | 5            
6     | 2          | 2           | 0        | Computer 2 - Общая компьютерная платформа - 1                                    | 6            
7     | 2          | 1           | 0        | Computer 2 - Launcher - 1                                                        | 7            
8     | 2          | 23          | 0        | Computer 2 - Дисплей - 1                                                         | 8             

Я предполагаю, что что-то не так с частью кода setw(80) << left << subsystem_table_data[i].name, но не могу найти проблему. И насколько я знаю, это не из-за общей ширины консоли, так как первая строка печатается просто отлично.


person Egor Chubarov    schedule 27.09.2016    source источник


Ответы (1)


setw() работает как задумано. Он дополняет вывод заданным количеством байтов1.

Проблема в том, что вам не нужно количество байтов, вам нужна ширина текста, но если ваш текст не является чисто ASCII и напечатан моноширинным шрифтом, эти две вещи разные.

Различия возникают на нескольких уровнях:

  1. Кодовые точки Unicode выше диапазона ASCII кодируются (при условии, что UTF-8; похоже, что вывод находится в UTF-8 из-за количества байтов, которые занимают различные строки) как несколько байтов.
  2. Несколько кодовых точек могут объединяться в один глиф. Если вы используете составленную нормальную форму, которая является стандартной, за исключением файловой системы MacOS, все кириллические глифы имеют одну кодовую точку, но в разложенной форме «й» будет две.
  3. Глифы могут занимать разную ширину экрана.

Первый пункт - это то, что вызывает ваше смещение, возможно, в сочетании со вторым, если в вашем вводе могут появиться разложенные символы. Пока вы используете моноширинный шрифт, третий не беспокоит кириллицу, но если есть шанс, что вы встретите текст на китайском/японском/корейском языке, имейте в виду, что их глифы в основном «полноширинные» и на большинстве терминалов они занимают место двух латинских или кириллических букв.

Стандартная библиотека C++ не поддерживает подсчет глифов. Вам нужно будет использовать библиотеку поддержки Unicode (например, ICU) и выполнить выравнивание самостоятельно или воспользоваться простым выход и сделайте текстовую колонку последней, так что ее конец не будет иметь значения.


1 Документация для operator‹‹(std::ostream& , std::string const&) описывает эффект width() в терминах std::string::size(), и это определенно байты.

person Jan Hudec    schedule 27.09.2016