Перемотка std::cout для возврата к началу строки

Я пишу инструмент командной строки для Mac OS X, который обрабатывает кучу файлов. Я хотел бы показать пользователю текущий обрабатываемый файл, но не хочу, чтобы огромное количество файлов загрязняло окно терминала.

Вместо этого я хотел бы использовать одну строку для вывода пути к файлу, а затем повторно использовать эту строку для следующего файла. Есть ли символ (или какой-то другой код) для вывода в std::cout для выполнения этого?

Кроме того, если бы я захотел перенацелить этот инструмент на Windows, было бы решение одинаковым для обеих платформ?


person fbrereto    schedule 16.06.2010    source источник


Ответы (4)


«\r» должен работать как для Windows, так и для Mac OS X.

Что-то вроде:

std::cout << "will not see this\rwill see this" << std::flush;
std::cout << std::endl; // all done
person Logan Capaldo    schedule 16.06.2010
comment
... и Linux, и почти все остальное. Я думаю, что единственная платформа, с которой теоретически можно столкнуться и которая не будет правильно с этим справляться, — это версии Mac OS до X. - person Pavel Minaev; 17.06.2010
comment
Это будет на самом деле печатать, увидит это это - person Algoman; 18.08.2017
comment
Я имею в виду, что я также ожидал, что это сработает, но затем я поместил ваш код в cpp.sh, и он вернул не увидит этоувидит это, см. cpp.sh/3kgrm . Это ошибка на стороне cpp.sh? Думал, что они используют новейшие стандартные компиляторы. - person Aziuth; 24.10.2018
comment
Не делайте flush перед endl, так как это включает неявный flush. На самом деле, когда вам не нужно выводить немедленно, вообще не используйте endl или flush. Для разрыва строки просто используйте '\n'. Поток будет очищен самое позднее, когда он будет уничтожен. - person eike; 31.10.2019
comment
@Aziuth: проблема не в компиляторе, а в том, что код cpp.sh для получения стандартного вывода процесса и его рендеринга в браузере не поддерживает \r, как это сделал бы обычный терминал командной строки. - person Tony Delroy; 16.05.2020

У меня нет доступа к Mac, но с точки зрения чистой консоли это будет во многом зависеть от того, как он обрабатывает символы возврата каретки и перевода строки. Если вы можете буквально отправить одно или другое на консоль, вы хотите отправить просто возврат каретки.

Я почти уверен, что Mac обрабатывает как возврат каретки, так и перевод строки иначе, чем * nix и windows.

Если вы ищете обновления на месте (например, перезаписать текущую строку), я бы порекомендовал посмотреть библиотеку curses. Это должно предоставить независимые от платформы средства для выполнения того, что вы ищете. (потому что даже при использовании стандартного С++ нет независимых от платформы средств того, о чем вы просите).

person Nathan Ernst    schedule 16.06.2010
comment
Mac раньше относился к CR иначе, чем к Win/Unix, в те дни, когда это не был сам Unix. Но теперь это так, и этой проблемы больше нет. - person Pavel Minaev; 17.06.2010

Как говорится в ответе Натана Эрнста, если вам нужен надежный и правильный способ сделать это, используйте проклятия, в частности ncurses< /а>.

Если вам нужен простой хакерский способ, который работает, продолжайте...

Терминалы командной строки для Linux, UNIX, MacOS, Windows и т. д., как правило, поддерживают небольшой набор основных управляющих символов ASCII, включая десятичный символ 13, известный как возврат каретки и закодировано в C++ как '\r' или эквивалентно в восьмеричном '\015' или шестнадцатеричном '\x0D' - указание терминалу вернуться к начало линии.

Что вы обычно хотите сделать, это...

int line_width = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 80;
std::string spaces{line_width - 1, ' '};
for (const auto& filename : filenames) {
    std::cout << '\r' << spaces << '\r' << filename << std::flush;
    process_file(filename);
}
std::cout << std::endl; // move past last filename...

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

std::flush гарантирует, что программа C++ вызовет функцию ОС write() для отправки текст на терминал перед началом обработки файла. Без этого текст, необходимый для обновления — \r, пробелы, \r и имя файла — будет добавлен в буфер и записан только в ОС — например, в 4k куски — когда буфер заполнен, поэтому отображаемое имя файла будет отставать от фактического обрабатываемого файла на десятки файлов. Кроме того, скажем, буфер равен 4 КБ — 4096 байт — и в какой-то момент у вас буферизовано 4080 байт, затем выведите текст для следующего имени файла: в итоге вы получите \r и 15 пробелов в буфере, который при автоматической очистке будет в конечном итоге стереть первые 15 символов в строке на экране и оставить остальную часть предыдущего имени файла (если оно было длиннее 15 символов), а затем подождать, пока буфер снова не заполнится, прежде чем обновлять экран (все еще бессистемно).

Последний std::endl просто перемещает курсор из строки, где вы печатали имена файлов, чтобы вы могли написать «все готово» или просто оставить main() и вывести приглашение оболочки на красивую чистую строку, вместо того, чтобы потенциально перезаписывать часть вашего файла. последнее имя файла (отличные оболочки, такие как zsh, проверяют это).

person Tony Delroy    schedule 15.05.2020

std::cout интерпретирует "\r" как возврат к началу строки, если вы не хотите каждый раз добавлять "‹‹ endl", используйте "\n"

std::cout << "this will work!\nSee... a new line!" << std::endl;

person Marco    schedule 13.11.2014
comment
\n не является кросс-платформенной совместимостью (о чем fbrereto просил) окна фактически перемещают курсор только на одну строку вниз и остаются в том же столбце, когда вы печатаете \n вместо \r\n - person Algoman; 18.08.2017