#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define LEN 40
char* s_gets(char* st, int n);
int main(void) {
FILE* in, * out;
int ch;
char file_app[LEN];
char file_src[LEN];
int count = 0;
printf("enter name of file\n");
s_gets(file_app, LEN);
if ((in = fopen(file_app, "r")) == NULL) {
fprintf(stderr, "I couldn't open the file %s\n", file_app);
exit(EXIT_FAILURE);
}
strncpy(file_src, file_app, LEN - 5);
file_src[LEN - 5] = '\0';
strcat(file_src, ".txt");
if ((out = fopen(file_src, "w")) == NULL) {
fprintf(stderr, "can't create output file .\n");
exit(3);
}
while ((ch = getc(in)) != EOF) {
if (count++ % 3 == 0)
putc(ch, out);
}
if (fclose(in) != 0 || fclose(out) != 0)
fprintf(stderr, "Error in closing file\n");
return 0;
}
char* s_gets(char* st, int n) {
char* ret_val;
char* find;
ret_val = fgets(st, n, stdin);
if (ret_val) {
find = strchr(st, '\n');
if (find)
*find = '\0';
else
while (getchar() != '\n')
continue;
}
return ret_val;
}
按你的输入,txt文件必须跟exe在同一个文件夹。
或者输入txt文件的完整路径。
【相关推荐】
我们所写的一个程序中必须要有一个主函数 main,来表明函数开始的地方。当我们所实现的功能较少时,往往一个主函数就够了。随着我们实现功能的增加,只在主函数中实现使得主函数变得冗长且复杂,不利于后期的调试和维护,这个时候就需要子函数了。我们可以把一部分功能用子函数来实现,在主函数中调用子函数。当子函数因为实现的功能较多变得冗长且复杂时,可以把一部分功能用子函数来实现,称之为子函数的子函数。因此程序变得利于调试和维护。
下面的 test1.c 程序展现了这一过程:
// test1.c
int
fun (int num)
{
return num * 2;
}
int
main (void)
{
int local = 10;
int ret = local + fun (local);
return ret;
}
我们还可以把子函数 fun 的实现放在 main 函数的后面并且在 mian 函数之前加上 fun 函数的声明。
// test1.c
int fun (int num);
int
main (void)
{
int local = 10;
int ret = local + fun (local);
return ret;
}
int
fun (int num)
{
return num * 2;
}
这样在编译的时候,当解析到 main 函数中的 fun 函数调用时,编译程序就会向前寻找 fun 函数的实现或声明,当发现 fun 函数的声明时,编译程序就会知道 fun 函数的实现在主函数的之后,编译程序便会继续正常编译。
在一些编译器编译程序时,就算在 main 函数之后实现 fun 函数,main 函数之前不加 fun 函数的声明,也不会有出错信息,但正确的语法是要求加上的。
上面的程序结构安排使人感觉即利于修改又便于维护,但仔细一分析还是发现一个问题。
问题:当我写了一个 test2.c 程序文件,里面的 main 函数也需要调用 fun 函数时,我需要在 test2.c 程序文件在加入 fun 函数的实现。当我有许多 test 程序文件,里面的 main 函数都需要调用 fun 函数时,我需要在每一个 test 程序文件中都加入 fun 函数的实现。这样的工作重复而又无意义。
解决这个问题最简单的方法就是引入头文件。我可以写一个 test.h 的自定义头文件,把 fun 函数的实现放进去,每一个调用 fun 函数的 test 程序文件只需要引入头文件 test.h 即可。
这里需要解释一下编译第一阶段——预处理。预处理器(cpp)根据以字节#开头的命令,修改原始的C程序。比如 test.c 中第1行的 #include “test.h” 命令告诉预处理器读取自定义头文件 test.h 的内容,并把它直接插入到程序文本中。结果就得到了另一个C程序,通常是以 .i 作为文件扩展名。这里所说的插入是直接把整个系统头文件copy到程序文本#include的位置。并且加上一些标志信息。
下面代码展示:
// test.h
#ifndef TEST_H
#define TEST_H
int
fun (int num)
{
return num * 2;
}
#endif
// test.c
#include "test.h"
int
main (void)
{
int local = 10;
int ret = local + fun (local);
return ret;
}
经过预处理后,得到 test.i 程序文件。
// test.i
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"
# 1 "test.h" 1
int
fun (int num)
{
return num * 2;
}
# 2 "test.c" 2
int
main (void)
{
int local = 10;
int ret = local + fun (local);
return ret;
}
可以发现 test.h 自定义头文件被复制到了原先 #include 的位置,并且加上了一些标志信息。因为我们的 #include 在主函数之前,所以在 test.i 程序文件中,fun 函数的实现在 main 函数的前面,因此我们不需要任何函数声明。
这样做很快就会发现另一个问题。
问题:当我因为需要修改 main 函数后,在重新编译的过程中,因为 test.c 文件中有 #include “test.h”,预处理后得到 test.i 预处理文件,fun 函数的实现被复制到 test.i 文件中,fun 函数也需要重新编译一遍。当 fun 函数的实现较为简单时,这样做没有太大的影响,但是当 fun 函数的实现较为复杂时,这样做十分不利于调试和维护。
解决这个问题最好的方法就是引入 fun.c 文件,把 fun 函数的实现放在 fun.c 文件中,在 test.h 自定义头文件中放入 fun 函数的声明。代码如下:
// fun.c
int
fun (int num)
{
return num * 2;
}
// test.h
#ifndef TEST_H
#define TEST_H
extern int fun (int num);
#endif
// test.c
#include "test.h"
int
main (void)
{
int local = 10;
int ret = local + fun (local);
return ret;
}
这样做我们可以对 test.c 和 fun.c 这两个文件分开预处理、编译、汇编。当得到 fun.o 和 test.o 两个可重定位目标文件时,用链接器将这两个文件连接成可执行目标文件 test 。
gcc -E test.c -o test.i // 预处理
gcc -S test.i -o test.s // 编译
gcc -c test.s -o test.o // 汇编
gcc -E fun.c -o fun.i // 预处理
gcc -S fun.i -o fun.s // 编译
gcc -c fun.s -o fun.o // 汇编
gcc fun.o test.o -o test // 链接
当我们需要修改 main 函数时,只需要对 test.c 文件重新预处理、编译、汇编。得到 fun.o 后重新与 test.o 链接一下就可以得到可执行目标文件 test 。
因为 main 函数中调用了 fun 函数,而 fun 函数的实现又没有与 main 函数在同一文件中,所以需要在 test.c 文件中 main 函数之前加上 fun 函数的外部声明,以此来表明 fun 函数的实现在另一个文件中。而 fun 函数的声明在自定义头文件 test.h 中,所以在 test.c 文件中 mian 函数之前加上 #include “test.h” 。
查看预处理 test.c 后得到的 test.i 文件可以检验这个观点。
// test.i
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"
# 1 "test.h" 1
extern int fun (int num);
# 2 "test.c" 2
int
main (void)
{
int local = 10;
int ret = local + fun (local);
return ret;
}
如果在 test.c 文件中没有 #include “test.h” ,在编译过程中便会出现警告信息:warning : implicit declaration of function ‘fun’
如果查看过系统头文件,便可以发现:在系统头文件中都是一些函数声明和宏定义,而没有任何函数的实现。我们调用库函数时必须要包含特定的系统头文件,就是因为系统头文件中有我们使用库函数的外部声明。
拿 hello.c 程序文件来举例,代码如下:
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
因为我们的程序文件中调用了库函数 printf ,所以需要包含头文件 #include <stdio.h> ,因为系统头文件 stdio.h 中有 printf 函数的声明,预处理得到 hello.i 文件后在 main 函数之前便会有 printf 函数的外部声明,接下来的编译过程便不会出现警告信息。当经过汇编阶段得到 hello.o 可重定位目标文件后,编译器会自动链接 printf.o 可重定位文件,最终生成可执行目标文件。
当我们修改 main 函数后重新编译时,编译器会把 hello.c 程序文件重新预处理、编译、汇编得到 hello.o 可重定位目标文件,然后与 printf.o 链接生成可执行目标文件。编译器当然不会重新预处理、编译、汇编 printf.c 文件。