C++中cout的printf格式化等价物

3
我是一名计算机科学专业的学生,今天我收到了一个非常特别的作业,需要使用C++编写。直到今天我一直在学习C语言,对于这个任务来说有些盲目。
在C语言中,我通常会使用以下内容:
printf("\n\n\t%-30s %-7d liters\n\t%-30s %-7d liters\n\t%-30s %-7d km",
       "Current gasoline in reserve:",
       db.currentGas,
       "Total gasoline used:",
       db.usedGas,
       "Total travel distance:",
       db.usedGas);

由于作业要求用C++编写,所以我尝试了以下代码:

cout << setw(30) << "\n\n\tCurrent gasoline in reserve: "
     << setw(7) << db.currentGas << "litres"
     << setw(30) << "\n\tTotal gasoline used: "
     << setw(7) << db.usedGas << "litres"
     << setw(30) << "\n\tTotal travel distance: "
     << setw(7) << db.travelDistance << "km";

但是似乎 C 语言中的 %-30s 和 C++ 语言中的 setw(30) 存在一些差异?


4
我永远不会理解为什么有些人会对一个完全有效的问题投负分,而这个问题还包括已经尝试过的代码和清晰地表达问题的具体细节。是恶意用户吗? - user2261062
1
实际上,在C++中使用printf是完全合法的...不过,你最好使用#include <cstdio>而不是#include <stdio.h>。我个人经常使用printf(有时也用(s)scanf)函数,因为它比C++流更方便。另一方面,如果涉及到复杂的类或者typedef在所有涉及平台上都不相同(例如uint32_t),后者会非常方便。因此,如果任务要求仅使用C++,则可以保留对printf的调用 - 如果要求明确使用流,则需要进行更改... - Aconcagua
@Aconcagua:仍然是一个好的实践去避免使用printf() / scanf()函数族,因为它们可能会引起致命错误(由于它们是可变参数函数,即没有类型检查)。此外,即使在C语言中,我也会远离任何scanf() -- 它只比gets()稍微容易一点点... - DevSolar
@DevSolar 说实话,我通常不使用scanf - 更喜欢一次读取整行(fgets),然后使用sscanf - 如果我做...不要误解,我非常欣赏C++流。但有时候,好老的printf家族比流更方便,比如: fprintf(stderr, "[...] failed with error %d (%s)\n", errno, strerror(errno)); vs. std::cerr << "[...] failed with error " << errno << " (" << strerror(errno) << ')' << std::endl;. 如果已经有代码可以正确工作,我会遵循“不要触碰运行中的系统”的原则,继续使用它。 - Aconcagua
@Aconcagua:同意,如果它存在并通过测试,请不要去碰它。但在编写新代码时,我会尽可能避免这种结构的使用。 - DevSolar
3个回答

6

事实上有一个区别,像这样:

Georgioss-MacBook-Pro:~ gsamaras$ g++ -Wall main.cpp
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out 


    Current gasoline in reserve:       6litres       
    Total gasoline used:       5litres     
    Total travel distance:       4kmGeorgioss-MacBook-Pro:~ gsamaras$ gcc -W
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out 


    Current gasoline in reserve:   6       liters
    Total gasoline used:           5       liters
    Total travel distance:         4       kmGeorgioss-MacBook-Pro:~ gsamaras$ 

但问题是,差异在哪里?

setw(30)等同于%30s但是,您使用了-30s,这会将输出左对齐!为了获得类似的行为,请使用std::left,像这样:

cout << "\n\n" << left << setw(30) << "\tCurrent gasoline in reserve: " << left << setw(7) << 6 << "litres\n" << left << setw(30) << "\tTotal gasoline used: " << left << setw(7) << 5 << "litres\n" << left << setw(30) << "\tTotal travel distance: " << left << setw(7) << 4 << "km";

非常感谢您的详细解释!如果我的问题有点愚蠢,我很抱歉,因为我完全是C++的新手 :) - TenzoNakami
3
几年前,当我在大学开始学习C ++时,我和你处于同样的水平,所以没有愚蠢的问题,我给@TenzoNakami点了个赞! =)不客气。 - gsamaras
1
应该是cout << "\n\n\t" << setw(30) << left << "当前汽油...",因为您不想计算换行符/制表符。 - Klitos Kyriacou
确实,@KlitosKyriacou,我正要修改我的答案,但还是谢谢你! :) - gsamaras
感谢大家的支持!我从上大学的第一天开始就一直在使用StackOverflow,但社区总是令人惊叹 :) 感谢您支持像我这样正在学习曲线上的人! - TenzoNakami

3

printf 中使用连字符会使文字左对齐。

如果想在 C++ 中实现这个效果,可以使用 std::left


1
根据gsamaras的答案,这的确是一个正确的答案。但更详细的解释总是更受欢迎的! :) - TenzoNakami

0
在 C++20 中,您可以使用 std::format 完成此操作,它非常容易从 printf 进行转换:
std::cout << std::format(
       "\n\n\t{:30} {:<7} liters\n\t{:30} {:<7} liters\n\t{:30} {:<7} km",
       "Current gasoline in reserve:",
       db.currentGas,
       "Total gasoline used:",
       db.usedGas,
       "Total travel distance:",
       db.usedGas);

与此同时,您可以使用基于其构建的{fmt}库std::format。{fmt}还提供了print函数,使这一过程更加简单和高效(godbolt):
fmt::print(
       "\n\n\t{:30} {:<7} liters\n\t{:30} {:<7} liters\n\t{:30} {:<7} km",
       "Current gasoline in reserve:",
       db.currentGas,
       "Total gasoline used:",
       db.usedGas,
       "Total travel distance:",
       db.usedGas);

免责声明:我是 {fmt} 和 C++20 std::format 的作者。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接