C语言中的 %*s 和 %.*s 和C++的字符串格式化输出

文章目录

    • 准备工作:先记住两个概念
    • [第一个:`%*s` 中的 `*` 是什么意思?](#第一个:%*s 中的 * 是什么意思?)
      • [先看不带 `*` 的普通写法(你肯定见过):](#先看不带 * 的普通写法(你肯定见过):)
      • [再看带 `*` 的写法:](#再看带 * 的写法:)
      • 完整运行代码:
    • [第二个:`%.*s` 中的 `.*` 是什么意思?](#第二个:%.*s 中的 .* 是什么意思?)
      • [先看不带 `*` 的普通写法:](#先看不带 * 的普通写法:)
      • [再看带 `.*` 的写法:](#再看带 .* 的写法:)
      • 完整运行代码:
    • [第三个:两个一起用 `%*.*s`](#第三个:两个一起用 %*.*s)
    • [第四个:`scanf` 中的 `%*s`(完全不同!)](#第四个:scanf 中的 %*s(完全不同!))
    • 总结(初学者记住这些就够了)
    • 最简单的记忆方法
    • [一、最传统的方法:`cout <<`(最常用,但功能有限)](#一、最传统的方法:cout <<(最常用,但功能有限))
    • [二、控制宽度和对齐:`iomanip` 库](#二、控制宽度和对齐:iomanip 库)
      • [1. 控制宽度:`setw()`](#1. 控制宽度:setw())
      • [2. 左对齐和右对齐:`left` 和 `right`](#2. 左对齐和右对齐:leftright)
      • [3. 填充字符:`setfill()`](#3. 填充字符:setfill())
    • [三、截取字符串(相当于 C 的 `%.*s`)](#三、截取字符串(相当于 C 的 %.*s))
    • [四、现代 C++ 方法:`std::format`(C++20,推荐!)](#四、现代 C++ 方法:std::format(C++20,推荐!))
      • [如果你用不了 C++20(编译器太老)](#如果你用不了 C++20(编译器太老))
    • [五、C 和 C++ 对比(初学者友好版)](#五、C 和 C++ 对比(初学者友好版))
    • 六、实际例子对比
    • 总结:初学者怎么选?

准备工作:先记住两个概念

  • 宽度:一共占多少个字符的位置
  • 精度:最多取多少个字符

第一个:%*s 中的 * 是什么意思?

* 的作用是:宽度不写死,用一个变量来决定

先看不带 * 的普通写法(你肯定见过):

c 复制代码
printf("%10s", "hello");

输出:" hello"(一共10个位置,前面补5个空格)

再看带 * 的写法:

c 复制代码
int w = 10;
printf("%*s", w, "hello");

输出:" hello"(和上面完全一样)

解释

  • %*s 中的 * 会被 w 替换成 10
  • 相当于 printf("%10s", "hello")

完整运行代码:

c 复制代码
#include <stdio.h>

int main() {
    int width = 15;
    printf("【%*s】\n", width, "hi");
    return 0;
}

输出:

复制代码
【            hi】

hi 占了15个位置,前面补了13个空格(因为 hi 本身2个字符,15-2=13)


第二个:%.*s 中的 .* 是什么意思?

.* 的作用是:最多取几个字符,用一个变量来决定

先看不带 * 的普通写法:

c 复制代码
printf("%.3s", "hello");

输出:"hel"(只取前3个字符)

再看带 .* 的写法:

c 复制代码
int n = 3;
printf("%.*s", n, "hello");

输出:"hel"(和上面一样)

解释

  • %.*s 中的 .* 会被 n 替换成 3
  • 相当于 printf("%.3s", "hello")

完整运行代码:

c 复制代码
#include <stdio.h>

int main() {
    int len = 4;
    printf("【%.*s】\n", len, "abcdef");
    return 0;
}

输出:

复制代码
【abcd】

只取了前4个字符 "abcd"


第三个:两个一起用 %*.*s

同时控制宽度取几个字符

c 复制代码
#include <stdio.h>

int main() {
    int width = 10;   // 一共占10个位置
    int len = 4;      // 最多取4个字符
    
    printf("【%*.*s】\n", width, len, "hello world");
    return 0;
}

输出:

复制代码
【      hell】

分解步骤:

  1. 先取前4个字符 → "hell"
  2. 再占10个宽度 → 前面补6个空格
  3. 结果:" hell"

第四个:scanf 中的 %*s(完全不同!)

重要scanf 里的 %*sprintf 里的 %*s 是两回事!

scanf 中,* 表示跳过这个数据,不保存。

c 复制代码
#include <stdio.h>

int main() {
    char word[100];
    
    // 输入字符串:"apple banana"
    sscanf("apple banana", "%*s %s", word);
    //                    ↑ 跳过第一个字符串 "apple"
    //                                 ↑ 把第二个 "banana" 存到 word 中
    
    printf("%s\n", word);  // 输出 "banana"
    return 0;
}

再举个例子:

c 复制代码
sscanf("100 200 300", "%*d %d %*d", &a);
// 跳过100,读取200到a,跳过300

总结(初学者记住这些就够了)

写法 用在 意思 例子
%*s printf 宽度由变量决定 printf("%*s", w, s)
%.*s printf 取几个字符由变量决定 printf("%.*s", n, s)
%*.*s printf 宽度和取字符数都由变量决定 printf("%*.*s", w, n, s)
%*s scanf 跳过这个字符串 scanf("%*s %s", s)

最简单的记忆方法

  • printf 里的 * = 数字不写死,用变量代替
  • scanf 里的 * = 跳过不要了

这样是不是清楚多了?


好的,C++ 的格式化比 C 安全很多,也更好理解。我从最简单到最现代,逐个给你介绍。


一、最传统的方法:cout <<(最常用,但功能有限)

cpp 复制代码
#include <iostream>
#include <string>

int main() {
    int age = 18;
    std::string name = "小明";
    
    // 直接拼接输出
    std::cout << "我叫" << name << ",今年" << age << "岁" << std::endl;
    // 输出:我叫小明,今年18岁
    
    return 0;
}

特点

  • 自然、好理解
  • 不需要记 %d%s 这些格式
  • 但控制对齐、宽度比较麻烦

二、控制宽度和对齐:iomanip

需要 #include <iomanip>

1. 控制宽度:setw()

cpp 复制代码
#include <iostream>
#include <iomanip>

int main() {
    std::cout << std::setw(10) << "hello" << std::endl;
    // 输出:"     hello"(一共10个位置,右对齐,前面补5个空格)
    
    return 0;
}

2. 左对齐和右对齐:leftright

cpp 复制代码
#include <iostream>
#include <iomanip>

int main() {
    // 右对齐(默认)
    std::cout << std::setw(10) << "hello" << std::endl;
    // 输出:"     hello"
    
    // 左对齐
    std::cout << std::left << std::setw(10) << "hello" << std::endl;
    // 输出:"hello     "
    
    // 中间的换回右对齐
    std::cout << std::right << std::setw(10) << "world" << std::endl;
    // 输出:"     world"
    
    return 0;
}

3. 填充字符:setfill()

cpp 复制代码
#include <iostream>
#include <iomanip>

int main() {
    // 用 * 号填充
    std::cout << std::setfill('*') << std::setw(10) << "hello" << std::endl;
    // 输出:"*****hello"
    
    // 用 0 填充
    std::cout << std::setfill('0') << std::setw(8) << 123 << std::endl;
    // 输出:"00000123"
    
    return 0;
}

三、截取字符串(相当于 C 的 %.*s

C++ 用 substr() 来截取,比 C 简单多了:

cpp 复制代码
#include <iostream>
#include <string>

int main() {
    std::string s = "hello world";
    
    // 取前5个字符
    std::cout << s.substr(0, 5) << std::endl;
    // 输出:"hello"
    
    // 结合宽度控制
    std::cout << std::setw(10) << s.substr(0, 5) << std::endl;
    // 输出:"     hello"(先取5个字符,再占10个宽度)
    
    return 0;
}

四、现代 C++ 方法:std::format(C++20,推荐!)

最像 Python 的 f-string,非常好用!

cpp 复制代码
#include <iostream>
#include <format>  // C++20

int main() {
    std::string name = "小明";
    int age = 18;
    double score = 95.5;
    
    // 基本用法
    std::cout << std::format("我叫{},今年{}岁", name, age) << std::endl;
    // 输出:我叫小明,今年18岁
    
    // 控制宽度和位置
    std::cout << std::format("{:10}", "hello") << std::endl;
    // 输出:"hello     "(左对齐,宽度10)
    
    std::cout << std::format("{:>10}", "hello") << std::endl;
    // 输出:"     hello"(右对齐)
    
    std::cout << std::format("{:*<10}", "hello") << std::endl;
    // 输出:"hello*****"(左对齐,用*填充)
    
    // 截取字符串
    std::cout << std::format("{:.5}", "hello world") << std::endl;
    // 输出:"hello"(只取前5个字符)
    
    // 组合使用
    std::cout << std::format("{:>10.5}", "hello world") << std::endl;
    // 输出:"     hello"(取前5个字符,右对齐宽度10)
    
    return 0;
}

如果你用不了 C++20(编译器太老)

可以装 {fmt} 库,用法和 std::format 一模一样:

cpp 复制代码
#include <fmt/core.h>  // 第三方库

int main() {
    fmt::print("我叫{},今年{}岁\n", "小明", 18);
    return 0;
}

五、C 和 C++ 对比(初学者友好版)

需求 C 写法 C++ 传统写法 C++ 现代写法(C++20)
输出变量 printf("%d", age) cout << age format("{}", age)
固定宽度 printf("%10s", s) cout << setw(10) << s format("{:10}", s)
左对齐 printf("%-10s", s) cout << left << setw(10) << s format("{:<10}", s)
右对齐 printf("%10s", s) cout << right << setw(10) << s format("{:>10}", s)
补0 printf("%08d", n) cout << setfill('0') << setw(8) << n format("{:08}", n)
截取字符串 printf("%.5s", s) s.substr(0,5) format("{:.5}", s)
跳过输入 scanf("%*s %s", s) cin >> skip >> s(不常用) cin 逐个读取

六、实际例子对比

同样功能,三种写法:

C 语言(麻烦、容易错):

c 复制代码
printf("%10s\n", "hello");
printf("%.3s\n", "hello world");
printf("%*.*s\n", 10, 5, "hello world");

C++ 传统(有点啰嗦):

cpp 复制代码
cout << setw(10) << "hello" << endl;
cout << string("hello world").substr(0, 3) << endl;
cout << setw(10) << string("hello world").substr(0, 5) << endl;

C++20 现代(最简洁):

cpp 复制代码
cout << format("{:10}\n", "hello");
cout << format("{:.3}\n", "hello world");
cout << format("{:>10.5}\n", "hello world");

总结:初学者怎么选?

  1. 刚入门 :用 cout << 直接输出,简单够用
  2. 需要对齐表格 :用 setw()left/right
  3. 编译器支持 C++20 :直接学 std::format,最好用
  4. 工作项目 :看项目用什么,一般是 coutstd::format

一句话 :如果你用的编译器比较新(VS2022、gcc13+),学 std::format 就行,它最像 Python,最容易用!

相关推荐
消失的旧时光-19431 小时前
C语言对象模型系列(四)《Linux 内核里的 container_of 到底是什么黑魔法?》—— 一篇讲透 Linux 内核的“对象模型”核心技巧
linux·c语言·算法
螺丝钉的扭矩一瞬间产生高能蛋白2 小时前
QT的C++接口基础用法
c++·qt·嵌入式软件·嵌入式linux·linux应用
2501_931803752 小时前
Go:一门为解决C语言痛点而生的现代语言
c语言·开发语言·golang
智者知已应修善业2 小时前
【51单片机模拟生日蜡烛】2023-10-10
c++·经验分享·笔记·算法·51单片机
智者知已应修善业2 小时前
【51单片机如何让LED灯从一亮到八,再从八亮到一】2023-10-13
c++·经验分享·笔记·算法·51单片机
qeen873 小时前
【数据结构】二叉树相关经典函数C语言实现
c语言·数据结构·c++·笔记·学习·算法·二叉树
良木生香3 小时前
【C++初阶】STL——List从入门到应用完全指南(1)
开发语言·数据结构·c++·程序人生·算法·蓝桥杯·学习方法
aqiu1111114 小时前
【并查集专题top】
c++·算法