Linux C格式化输出,如何掌握格式化字符串?

99ANYc3cd6
预计阅读时长 27 分钟
位置: 首页 C语言 正文
  1. 输入/输出的格式化:使用 printfscanf 系列函数,按照指定的格式字符串来读写数据。
  2. 字符串的格式化:使用 sprintfsnprintf 等函数,将格式化后的数据写入字符串缓冲区。

我会从基础到进阶,并结合代码示例为你详细解释。

linux c语言 格式化
(图片来源网络,侵删)

格式化输出 (printf 系列)

printf 是 C 语言中最常用的格式化输出函数,它位于 stdio.h 头文件中。

1 基本语法

int printf(const char *format, ...);
  • format:格式化字符串,它包含了两种类型的字符:
    • 普通字符:直接原样输出。
    • 格式说明符:以 开头,用于指定后面参数的类型和输出格式。
  • 可变参数列表,数量和类型必须与格式字符串中的格式说明符一一对应。

2 常用格式说明符

说明符 类型 示例
%d%i 有符号十进制整数 printf("Number: %d", 42);
%u 无符号十进制整数 printf("Unsigned: %u", 42);
%f 单精度浮点数 printf("Float: %f", 3.14);
%lf 双精度浮点数 (用于 scanf 输入时是 %lfprintf 输出时用 %f 也可以,但推荐 %lf 保持一致性) printf("Double: %lf", 3.14159);
%c 单个字符 printf("Char: %c", 'A');
%s 字符串 (以 \0 结尾的字符数组) printf("String: %s", "Hello");
%p 指针地址 printf("Address: %p", &my_var);
%x%X 十六进制整数 (小写/大写) printf("Hex: %x", 255); // 输出 ff
%o 八进制整数 printf("Octal: %o", 8); // 输出 10
输出一个百分号 printf("100%%"); // 输出 100%

3 修饰符

在 和类型字母之间可以添加修饰符,来控制输出格式。

  • 宽度:指定输出的最小字符数。
    printf("%5d", 42);   // 输出 "   42" (右对齐,总宽度为5)
    printf("%-5d", 42);  // 输出 "42   " (左对齐,总宽度为5)
  • 精度
    • 对于浮点数 (%f, %lf),指定小数点后的位数。
      printf("%.2f", 3.14159); // 输出 "3.14"
    • 对于字符串 (%s),指定最大字符数。
      printf("%.5s", "HelloWorld"); // 输出 "Hello"
    • 对于整数 (%d),指定数字的最小位数(不足补前导0)。
      printf("%05d", 42); // 输出 "00042"
  • 标志
    • 总是显示符号(正数显示 ,负数显示 )。
      printf("%+d", 42);  // 输出 "+42"
      printf("%+d", -42); // 输出 "-42"
    • ` (空格):正数前加空格,负数前加-`。
      printf("% d", 42);  // 输出 " 42"
      printf("% d", -42); // 输出 "-42"
    • 对于八进制和十六进制,添加前缀。
      printf("%#o", 10); // 输出 "012"
      printf("%#x", 255); // 输出 "0xff"

4 printf 示例

#include <stdio.h>
int main() {
    int age = 30;
    float height = 1.75f;
    double pi = 3.1415926535;
    char initial = 'J';
    char name[] = "Linux C";
    // 基本输出
    printf("Name: %s\n", name);
    printf("Age: %d\n", age);
    printf("Height: %.2f meters\n", height); // 保留两位小数
    // 使用修饰符
    printf("Pi (with 4 decimal places): %.4lf\n", pi);
    printf("Padded Age (5 digits): %05d\n", age);
    printf("Left-aligned Age (5 digits): %-5d\n", age);
    printf("Signed Age: %+d\n", age);
    // 指针地址
    int num = 100;
    printf("Address of 'num': %p\n", (void*)&num); // 强制转换为void*是良好实践
    return 0;
}

编译和运行

gcc -o format_example format_example.c
./format_example

格式化输入 (scanf 系列)

scanf 用于从标准输入(通常是键盘)读取数据,并根据格式字符串进行解析,它也位于 stdio.h 中。

linux c语言 格式化
(图片来源网络,侵删)

1 基本语法

int scanf(const char *format, ...);
  • format:格式化字符串,包含普通字符和格式说明符。
  • 可变参数列表,必须是指针,用于存储读取到的数据。

2 关键点:必须使用指针

这是初学者最容易犯错的地方。scanf 需要知道要把读到的数据存到哪里去,所以你必须传递变量的地址。

int number;
scanf("%d", &number); // 正确,传递 number 的地址
// scanf("%d", number); // 错误!传递的是值,无法接收数据

3 常用格式说明符

printf 类似,但注意浮点数在 scanf 中必须使用 %lf 来读取 double 类型。

说明符 对应的变量类型
%d int *
%f float *
%lf double *
%c char *
%s char * (会读取直到遇到空白字符)
%x int * (读取十六进制)

4 scanf 示例

#include <stdio.h>
int main() {
    int id;
    char name[50];
    double salary;
    printf("Enter your ID: ");
    scanf("%d", &id);
    // 清除输入缓冲区中的换行符,防止它被下一个 %s 读取
    while (getchar() != '\n');
    printf("Enter your name: ");
    // 注意:scanf("%s", name) 不安全,不能处理带空格的字符串
    // 使用 fgets 更安全,但这里为了演示 scanf
    scanf("%49s", name); // 读取最多49个字符,防止溢出
    printf("Enter your salary: ");
    scanf("%lf", &salary); // 必须用 %lf 读取 double
    printf("\n--- Employee Info ---\n");
    printf("ID: %d\n", id);
    printf("Name: %s\n", name);
    printf("Salary: %.2lf\n", salary);
    return 0;
}

编译和运行

gcc -o scanf_example scanf_example.c
./scanf_example

交互式输入

linux c语言 格式化
(图片来源网络,侵删)
Enter your ID: 101
Enter your name: Linus Torvalds
Enter your salary: 1000000.50
--- Employee Info ---
ID: 101
Name: Linus
Salary: 1000000.50

你会发现 "Torvalds" 没有被读取,这就是 scanf("%s", ...) 的局限性。


字符串格式化

我们不想直接输出到屏幕,而是想把格式化后的数据存到一个字符串变量里。

1 sprintf (String Print Formatted)

sprintf 的行为和 printf 一样,只是它把结果写入一个字符数组(字符串),而不是输出到屏幕。

#include <stdio.h>
int main() {
    int x = 10, y = 20;
    char result_str[100]; // 确保缓冲区足够大
    // 将格式化后的字符串写入 result_str
    sprintf(result_str, "The sum of %d and %d is %d.", x, y, x + y);
    printf("Generated string: %s\n", result_str);
    return 0;
}

输出

Generated string: The sum of 10 and 20 is 30.

危险sprintf 不会检查目标缓冲区的大小,如果格式化后的字符串太长,会导致缓冲区溢出,这是一个严重的安全漏洞。

2 snprintf (Safe String Print Formatted)

snprintfsprintf 的安全版本,它增加了一个参数 size,用于指定目标缓冲区的大小。

  • 如果生成的字符串长度(包括结尾的 \0)小于 size,则完整写入。
  • 如果生成的字符串长度大于等于 size,则只写入 size - 1 个字符,并在最后添加 \0,确保不会溢出。

强烈推荐在所有新代码中使用 snprintf 替代 sprintf

#include <stdio.h>
int main() {
    int x = 123456789;
    char small_buffer[5]; // 故意设置一个很小的缓冲区
    // 使用 snprintf 是安全的
    // 它会写入 "1234",然后添加 '\0',总共 5 个字符
    snprintf(small_buffer, sizeof(small_buffer), "%d", x);
    printf("Small buffer content: %s\n", small_buffer); // 输出 "1234"
    printf("Buffer size is safe: %zu\n", sizeof(small_buffer)); // 输出 5
    return 0;
}

高级格式化

1 动态宽度与精度

你可以使用 作为占位符,在参数列表中动态指定宽度和精度。

#include <stdio.h>
int main() {
    int width = 10;
    int precision = 3;
    double value = 123.456789;
    // width 和 precision 的值由变量提供
    printf("Dynamic width and precision: %*.*f\n", width, precision, value);
    // 等价于 printf("Dynamic width and precision: %10.3f\n", value);
    return 0;
}

输出

Dynamic width and precision:    123.457

2 fprintffscanf

这两个函数可以将格式化输出/输入重定向到任何文件流,而不仅仅是标准输入/输出。

#include <stdio.h>
int main() {
    FILE *fp = fopen("data.txt", "w"); // 以写入模式打开文件
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }
    // 将格式化数据写入文件
    fprintf(fp, "ID: %d, Name: %s, Score: %.2f\n", 1, "Alice", 95.5);
    fclose(fp); // 关闭文件
    // 现在以读取模式打开文件
    fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }
    int id;
    char name[50];
    float score;
    // 从文件中读取格式化数据
    fscanf(fp, "ID: %d, Name: %s, Score: %f\n", &id, name, &score);
    printf("Read from file: ID=%d, Name=%s, Score=%.2f\n", id, name, score);
    fclose(fp);
    return 0;
}

总结与最佳实践

  1. 优先使用 snprintf:永远不要使用 sprintf,因为它不安全。snprintf 是现代 C 语言的标准做法。
  2. 浮点数输入/输出注意
    • printf 输出 double 时,用 %f%lf 都可以,但 %lf 更清晰。
    • scanf 输入 double 时,必须使用 %lf
  3. 处理字符串输入scanf("%s", ...) 无法处理带空格的字符串,对于用户输入的整行文本,fgets() 是更安全、更合适的选择。
  4. 检查返回值printfscanf 系列函数都返回成功处理的参数个数。scanf 的返回值可以用来判断是否成功读取了所有期望的数据。
    int num, count;
    printf("Enter an integer: ");
    count = scanf("%d", &num);
    if (count != 1) {
        printf("Invalid input!\n");
    }
  5. 清除输入缓冲区:在 scanf 读取数字后,换行符会留在缓冲区,可能会干扰后续的 scanf("%s", ...)fgets(),使用 while (getchar() != '\n'); 可以清除缓冲区直到遇到换行符。

希望这份详细的指南能帮助你完全掌握 Linux C 语言中的格式化!

-- 展开阅读全文 --
头像
C语言如何高效求1到100的和?
« 上一篇 今天
Linux C文件读写如何高效操作与避坑?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]