C语言处理文件名
** 代码是写给人看的,不是写给机器看的,只是顺便计算机可以执行而已 ——《计算机程序的构造和解释》**
首先放上这句话来说明写代码的时候不必过分的“咬文嚼字”。然而,这篇文章记录的似乎就是一个“咬文嚼字”的故事。
背景
事情是这样的,最近找到一个音频降噪的库,想要写个demo测试一下。程序流程很简单,打开文件 –> 读取数据 –> 处理数据 –> 保存到文件。 编译、运行,得到的结果也没什么问题,准备整理整理代码归个档。
突然发现程序中的输入文件”input.pcm”、输出文件 “output.pcm” 是不是有点太low了,更换了输入文件还要改名,每次的输出文件不做备份还会被覆盖,这看起来也太不“高级”了。
想想平时用过的一些开源工具,都是命令后面加上一些选项,看起来很“高级”,照着它们的模样搞起来。
使用main函数传参
使用main函数的参数传入输入文件和输出文件名也是很常见的做法,这种方法比较简单,代码如下:
int main(int argc, char *argv[ ])
{
printf("input: %s\n", argv[1]);
printf("output: %s\n", argv[2]);
return 0;
}
上述代码中,argv[1] 和 argv[2] 表示执行程序时的第2和第3个参数,编译程序,执行的时候加上输入输出文件名即可。
./test test.wav test_resample.wav
一切看起来正常,但是测了几次后发现每次输入两个文件名有点烦,就想改进一下程序,只给输入文件的名字,让程序自动生成输出文件的文件名。
自动生成输出文件名
这里涉及到简单的字符串处理,流程是,首先去掉 输入文件的后缀名,即只保留 ‘.’ 前面的内容,然后使用 strcat 函数接上相应的标识字符串即可。于是乎有了下面的代码。
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[ ])
{
int i;
printf("input: %s\n", argv[1]);
char output_file[128] = {0};
/* 拷贝'.'前面的内容 */
for (i = 0; argv[1][i] != '.'; i++)
output_file[i] = argv[1][i];
/* 连接上新的后缀 */
strcat(output_file, "_resample.wav");
printf("output: %s\n", output_file);
return 0;
}
编译运行,可以实现;
root@MRS:test# gcc -Wall test.c
root@MRS:test# ./a.out test.wav
output: test_resample.wav
本想这样移植到工程中,但看着for循环有点不舒服,就想有没有什么方法可以代替这个for循环。
使用标准函数
在 string.h 中找了一圈,发现 strncpy 和 strcspn。
**strncpy() ** 是比较常用的函数,功能是,拷贝字符串 src 前 n 个字符 到字符串 dest 中;
原型
char *strncpy(char *dest, const char *src, size_t n);
参数
- dest: 目标字符串指针
- src: 源字符串指针
- n: 要复制的字符数
返回值
返回复制后的目标字符串
**strcspn()**是一个比较陌生的函数,查了其描述:
原型
size_t strcspn(const char *str1, const char *str2);
参数
- str1:被检索的字符串
- str2:用于匹配的字符串
返回值
返回str1中连续不包含str2中字符的字符数。
参考描述,strcspn函数功能是 获得 str1 连续多少个字符不包括 str2 中的字符。说起来有点绕,举个例子:
str1 = “abcd1234”;
str2 = “3df”;
可以看到字符串 str1 中 ‘d’ 之前都没有包含 str2 中的任何字符,于是 strcspn(str1, str2); 返回 3。
于是新的代码为:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[ ])
{
char output_file[128];
printf("input: %s\n", argv[1]);
// 复制‘.’前的内容,strcspn(argv[1], ".")用于判断输入文件名'.'前的字符个数
strncpy(output_file, argv[1], strcspn(argv[1], ".") + 1);
// 连接上 标识符和后缀
strcat(output_file, "_resample.wav");
printf("output: %s\n", output_file);
return 0;
}
编译运行,没有问题。
root@MRS:test# gcc -Wall test.c
root@MRS:test# ./a.out test.wav
output: test_resample.wav
到此,强迫症已被治愈大半。但是,关闭 string.h 时突然又瞥见一个陌生的函数。
再简单一点
strtok
一个没怎么用过的函数,先看声明:
原型
char *strtok(char *str, const char *delim)
参数
- str : 要被分解的字符串
- delim : 包含分隔符的字符串
返回值
返回分解后的第一个子字符串,如果没有发现可检索的字符,返回NULL;
作用很简单,就是检索字符串 str 中有没有包含字符串 delim 中的字符,如果有,就将str分解,并返回分解得到的第一个子字符串。理解以后,继续修改代码
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[ ])
{
char *file_name = argv[1];
printf("input: %s\n", file_name);
strcat(strtok(file_name, "."), "_resample.wav");
printf("output: %s\n", file_name);
return 0;
}
root@MRS:test# gcc -Wall file_name.c
root@MRS:test# ./a.out test.wav
input: test.wav
output: test_resample.wav
编译、运行,没有问题,归档。