c - 如何删除包含用户在文件中提供的字符串的整行
问题描述
我需要删除包含用户插入的字符串的整行,我很难弄清楚如何删除这一行,这就是我到目前为止所绑定的。
例如,如果u.txt包含:
1 hello world
2 你在哪里
* 用户输入:你*
u.txt现在包含
1.hello world
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *tr1, *tr2;
char *p;
char chr;
char scanner [10];
int t = 1;
int sc;
tr1 = fopen("u.txt", "r");
chr = getc(tr1);
while (chr != EOF)
{
printf("%c", chr);
chr = getc(tr1);
}
printf("input your word to search:");
scanf("%s",scanner);
rewind(tr1);
tr2 = fopen("replica.txt", "w");
chr = 'A';
while (chr != EOF)
{
//expect the line to be deleted
sc= strstr(chr,scanner);
if (!sc)
{
//copy all lines in file replica.txt
putc(chr, tr2);
}
fclose(tr1);
fclose(tr2);
remove("u.txt");
rename("replica.txt", "u.txt");
tr1 = fopen("u.txt", "r");
chr = getc(tr1);
while (chr != EOF)
{
printf("%c", chr);
chr = getc(tr1);
}
fclose(tr1);
return 0;
}
解决方案
通过使用面向字符的输入和需要面向行的输入和输出fgetc()
的操作,您正在使自己变得困难。此外,在您的方法中,您将删除文件中包含包含较少子字符串的单词的每一行,例如:you
- “你”、“你的”、“你是”、“你自己”、“你已经”等等……
对于确保不删除包含单词的行的简单方法"you"
,您可以简单地检查以确保单词前后的字符是空格。您可以使用提供的宏来简化检查。isspace()
ctype.h
当您遇到面向行的问题时,请使用面向行的输入和输出函数,例如fgets()
POSIXgetline()
用于输入和puts()
/或fputs()
输出。唯一需要注意的是,面向行的输入函数读取并'\n'
在它们填充的缓冲区中包含尾随。只需在必要时'\n'
使用nul 终止字符'\0'
(或简单地0
)覆盖尾随即可删除。
使用面向行的方法将大大简化您的任务,并提供一种方便的方式,将您保留的行写入到输出文件中,而不是逐个字符地循环。使用面向字符的方法并没有错,而且它本身并不比面向行的方法慢——它只是不太方便。使工具与工作相匹配。
以下示例在删除不需要的行的情况下进行读取"u.txt"
和写入,同时保留单词是所查找单词的包含较少的子字符串的行。"v.txt"
(rename()
和remove()
留给您,因为您现有代码的那部分没有任何问题)
简单地使用足够大小的固定缓冲区是进行面向行输入的最简单方法。您通常会知道最大的预期线是什么,只需使用足以处理它的缓冲区(不要吝啬!)。大多数行的长度不超过 80-120 个字符。一个 1024 个字符的固定数组绰绰有余。只需声明一个数组用作缓冲区来保存读取的每一行,例如:
#define MAXW 32 /* max chars in word to find */
#define MAXC 1024 /* max chars for input buffer */
int main (int argc, char **argv) {
char buf[MAXC], /* buffer to hold each line */
word[MAXW]; /* user input to find/delete line */
size_t wordlen = 0; /* length of user input */
FILE *ifp, *ofp; /* infile & outfile pointers */
您可以验证是否在命令行上为输入和输出文件名提供了足够的参数,然后打开并验证您的每个文件,例如
if (argc < 3) { /* validate at least 2 program arguments given */
printf ("usage: %s infile outfile\n", strrchr (argv[0], '/') + 1);
return 1;
}
if (!(ifp = fopen (argv[1], "r"))) { /* open/validate open for reading */
perror ("fopen-ifp");
return 1;
}
if (!(ofp = fopen (argv[2], "w"))) { /* open/validate open for writing */
perror ("fopen-ofp");
return 1;
}
现在只需从用户那里获取输入并删除尾随'\n'
. 使用strcspn
您可以获得用户输入的长度,同时'\n'
在单个调用中覆盖尾随。请参阅:man 3 strspn,例如
fputs ("enter word to find: ", stdout); /* prompt for input */
fflush (stdout); /* optional (but recommended) */
if (!fgets (word, MAXW, stdin)) { /* read/validate word to find */
fputs ("(user canceled input)\n", stdout);
return 1;
}
/* get wordlen, trim trailing \n from find */
word[(wordlen = strcspn (word, "\n"))] = 0;
现在只需一次读取一行并在该行内搜索word
并验证它是一个完整的单词而不是一个更大单词的一部分。将所有不包含word
单独的行写入输出文件:
while (fgets (buf, MAXC, ifp)) { /* read each line */
char *p = strstr (buf, word); /* search for word */
if (!p || /* word not in line */
(p != buf && !isspace (*(p - 1))) || /* text before */
(!isspace (p[wordlen]) && p[wordlen] != 0)) /* text after */
fputs (buf, ofp); /* write line to output file */
}
(注意:标题中提供了isspace()
检查字符是否为空格的宏ctype.h
)
关闭这两个文件,除了你的remove()
,rename()
你就完成了。但是,请注意,您应该始终在写入fclose()
后进行验证,以确保您捕获与关闭时刷新文件流相关的任何错误,否则这些错误不会通过单独验证每个写入来捕获,例如
if (fclose (ofp) == EOF) { /* validate EVERY close-after-write */
perror ("fclose-ofp");
remove (argv[2]);
}
fclose (ifp); /* close input file */
}
添加所需的头文件,您就有了一个工作示例。您可以将示例按如下方式使用:
示例输入文件
$ cat dat/u.txt
hello world
how are you
show yourself
you're missing
示例使用
$ ./bin/filermline dat/u.txt dat/v.txt
enter word to find: you
生成的输出文件
$ cat dat/v.txt
hello world
show yourself
you're missing
仔细查看并比较面向行的方法与您使用面向字符的fgetc()
. 如果您还有其他问题,请告诉我。
推荐阅读
- c# - c#:点击事件在面板中绘制点
- java - Spring cloud discovery first 根本不起作用
- c++ - 在 R 中执行 C++ 代码
- ip - 以太网网络中的自动 IP 寻址不起作用
- ruby-on-rails - 尝试安装 ruby 机器时收到此错误“操作未成功完成,因为文件包含病毒或不需要的软件”
- c# - 从列表创建树视图
WPF - c# - unity player 位置加载错误
- javascript - 通过点击“mailto”链接跳转到页面顶部
- plsql - PL/SQL 中的异常处理和回滚
- javascript - 如何让 Vue 在表单字段中使用与 value 属性不同的值?