从 C 程序的输入文件中删除选定行的第二列
Delete second column for selected rows from the input file in C program
此程序的主要 objective:
打印第一列中的数字位于起始值和终止值之间的行,这取决于用户输入。但是开始和结束数字应该比较每行的每个第一个单词。
例如,
start = 770
stop = 791
它应该只比较每行的第一个整数。
删除每行的第二列并在每行的末尾添加(1 或任何数字),如示例输出格式。
请给我一些执行此操作的说明。
我尝试编写 C 代码,但没能成功。我附上它供您仔细阅读。
输入文件格式
766 102 1368 1369
767 102 1369 1370
768 102 1370 1371
769 102 1371 1372
770 102 1372 1373
771 102 1373 1374
772 102 1375 1376
773 102 1376 1377
774 102 1377 1378
775 102 1378 1379
776 102 1379 1380
777 102 1380 1381
778 102 1381 1382
779 102 1382 1383
780 102 1383 1384
781 102 1384 1385
782 102 1386 1387
783 102 1387 1388
784 102 1388 1389
785 102 1389 1390
786 102 1390 1391
787 102 1391 1392
788 102 1392 1393
789 102 1393 1394
790 102 1394 1395
791 102 1395 1396
717 204 339 1333 1341 1337
718 204 1337 1341 1342 1338
719 204 1338 1342 1335 355
720 204 1333 1334 1343 1341
721 204 1341 1343 1344 1342
722 204 1342 1344 1336 1335
输出格式
770 1372 1373 1
771 1373 1374 1
772 1375 1376 1
773 1376 1377 1
774 1377 1378 1
775 1378 1379 1
776 1379 1380 1
777 1380 1381 1
778 1381 1382 1
779 1382 1383 1
780 1383 1384 1
781 1384 1385 1
782 1386 1387 1
783 1387 1388 1
784 1388 1389 1
785 1389 1390 1
786 1390 1391 1
787 1391 1392 1
788 1392 1393 1
789 1393 1394 1
790 1394 1395 1
791 1395 1396 1
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE *file1;
FILE *out1;
///Function declaration
void OpenInputFile();
int Extract_SOLID_value( int To_Find_node, int To_Element_set_list);
int main()
{
int result;
OpenInputFile();
int ELEMENT_SOLID;
int STOP;
START = 10;
STOP = 100;
///Open txt file to write
out1 = fopen("output.txt", "w");
if (out1 == NULL)
{
perror("fopen");
exit(EXIT_FAILURE);
}
result = Extract_SOLID_value(START, STOP);
printf("%s", result);
fclose(file1);
fclose(out1);
return 0;
}/// end main
int Extract_SOLID_value( int To_Find_node, int To_Element_set_list)
{
char line[256];
int word;
if (!file1) {
perror("fopen");
exit(EXIT_FAILURE);
}
while (fscanf(file1,"%s%*[^\n]",word)){
if (word == START ){
fgets(line, sizeof (line),file1);
if (word == STOP){
return;
}
printf("%s \n", line);
fprintf(out1, "%s \n", line);
}
}
return 0;
}
void OpenInputFile(){
file1 = fopen("simple_CSDexample.dat", "r");
// check if file exists
if (!file1){
perror("fopen");
exit(EXIT_FAILURE);
}
return;
}
您已将 START
和 STOP
的值分别作为 To_Find_node
和 To_Element_set_list
赋给函数 Extract_SOLID_value()
,因此请在其中使用新名称该函数否则你会得到错误,因为 START
& STOP
不在该函数的范围内。
您检查了输入文件是否是在 OpenInputFile()
中创建的,所以为什么在 Extract_SOLID_value()
中的循环之前再次检查什么都没有改变?
当一行中的第一个值在所需范围内时,您需要继续扫描输入文件并将数据写入输出文件。
一个 fgets()
不行你必须依赖循环
int Extract_SOLID_value( int To_Find_node, int To_Element_set_list)
{
char line[256];
int word;
int fword;
while (fscanf(file1,"%d",&fword)==1){
if (fword >= To_Find_node && fword <=To_Element_set_list ){
fprintf(out1, "%d ", fword);
fprintf(stdout, "%d ", fword);
fscanf(file1, "%d", &word);
fgets(line, sizeof(line), file1);
line[strlen(line)-1]='[=10=]';
fprintf(out1, "%s 1\n", line);
fprintf(stdout, "%s 1\n", line);
}
else
{
fgets(line, sizeof(line), file1);
}
}
return 0;
}
fscanf()
将 return 成功分配给变量的数值。
只要在每次迭代开始时给 fword
赋值,循环就会继续下去。
因此 while
循环中的条件。
我们首先读取每行中的第一个数字,如果它在所需范围内,则将该数字与通过 fgets()
获得的该行的其余部分一起写入输出文件。
如果数字不在所需范围内,我们只是使用 fgets()
来跳过该行,但您也可以使用类似 fseek()
的内容。
fgets()
会将结尾的换行符 (\n
) 存储为已读入的字符串的一部分。
所以我们通过用 [=30=]
覆盖 \n
来标记字符串结尾来删除它。
您可以跳过带有 fprintf(stdout....)
的行。它相当于一个printf()
,我只是这样做是为了将写入文件的内容也打印到监视器上。
请记住,整数的格式说明符是 %d
而 不是 %s
.
有很多方法可以完成这个练习。除了基本的文件处理之外,主要目标是认识到您需要将列值处理为 integers 和 strings。 (第一列是唯一需要 整数 值的列)。
认识到您的比较需要一个 整数 值,您可以阅读整行,然后使用 sscanf
将第一列转换为 int
,或立即开始将每列 标记化 为字符串,并使用 strtol
将第一列转换为 int
。 (两种方式都可以)。
更大的问题是 标记 行。 (典型的选择是 strtok
——名副其实)。您也可以简单地使用一对指针和 "inch-worm" 从行的开始到结束,边走边挑选列。然而,对于简单的 space 或制表符分隔的列,strtok
使它变得容易。这是一种在一行中分隔未知数量的列的便捷方法,格式化输入函数将失败。
(注意:如果您需要保留具有 NULL 值的列,您还可以使用 strsep
函数——例如在您可能具有 col1,col2,,col4
的 .csv 文件中)
使用 strtok
和 strtol
执行到 int
的转换的实现可能如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* constants for max chars, start & stop values */
enum { BASE = 10, MAXC = 512, START = 770, STOP = 791 };
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* line buffer */
/* open file (or read from stdin -- default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
int val; /* 1st col value */
size_t len = strlen (buf); /* get buf len for validation */
char *p, *delim = " \t\n"; /* pointer & delimiters */
if (len && buf[len - 1] != '\n') { /* validate complete line */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXC);
return 1;
}
p = strtok (buf, delim); /* parse 1st column (as string) */
if (p == NULL) { /* handle 1st col error */
fprintf (stderr, "error: parse of first column failed.\n");
return 1;
}
errno = 0;
val = (int)strtol (p, NULL, BASE); /* convert to long/int */
if (errno) {
fprintf (stderr, "error: strtol() conversion failed.\n");
return 1;
}
/* for each line between START & STOP (inclusive) */
if (START <= val && val <= STOP) {
printf ("%d", val); /* output 1st col */
/* tokenize line, remove 2nd column, append 1 at end */
p = strtok (NULL, delim); /* parse/discard 2nd column */
if (!p) { /* handle failure to parse 2nd column */ }
p = strtok (NULL, delim); /* parse 3rd column */
if (!p) { /* handle failure to parse 3rd column */ }
while (p) { /* while token read */
printf (" %-8s", p); /* print token */
p = strtok (NULL, delim); /* parse next token/column */
}
printf ("1\n"); /* print final 1 at end */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
例子Use/Output
$ ./bin/parsecol <dat/columns.txt
770 1372 1373 1
771 1373 1374 1
772 1375 1376 1
773 1376 1377 1
774 1377 1378 1
775 1378 1379 1
776 1379 1380 1
777 1380 1381 1
778 1381 1382 1
779 1382 1383 1
780 1383 1384 1
781 1384 1385 1
782 1386 1387 1
783 1387 1388 1
784 1388 1389 1
785 1389 1390 1
786 1390 1391 1
787 1391 1392 1
788 1392 1393 1
789 1393 1394 1
790 1394 1395 1
791 1395 1396 1
使用 sscanf
将第一列转换为整数(并简单地将第一列重新读取为字符串)的第二个类似实现可能如下所示:
#include <stdio.h>
#include <string.h>
/* constants for max chars, start & stop values */
enum { MAXC = 512, START = 770, STOP = 791 };
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* line buffer */
/* open file (or read from stdin -- default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
int val; /* 1st col value */
size_t len = strlen (buf); /* get buf len for validation */
char *p, *delim = " \t\n"; /* pointer & delimiters */
if (len && buf[len - 1] != '\n') { /* validate complete line */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXC);
return 1;
}
if (sscanf (buf, "%d", &val) != 1) { /* convert 1st col to int */
fprintf (stderr, "error: conversion failure column 1.\n");
return 1;
}
/* for each line between START & STOP (inclusive) */
if (START <= val && val <= STOP) {
/* tokenize line, remove 2nd column, append 1 at end */
p = strtok (buf, delim); /* parse 1st column */
if (p == NULL) { /* handle 1st col error */
fprintf (stderr, "error: parse of first column failed.\n");
return 1;
}
printf ("%s", p); /* print column 1 */
p = strtok (NULL, delim); /* parse/discard 2nd column */
if (!p) { /* handle failure to parse 2nd column */ }
p = strtok (NULL, delim); /* parse 3rd column */
if (!p) { /* handle failure to parse 3rd column */ }
while (p) { /* while token read */
printf (" %-8s", p); /* print token */
p = strtok (NULL, delim); /* parse next token/column */
}
printf ("1\n"); /* print final 1 at end */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(注意:输出是一样的)
可以说,第一个更优雅一些,而在第二个中,您让 sscanf
处理转换和内部验证。
查看所有答案,如果您还有其他问题,请告诉我。
函数实现
针对您最后的评论,由于 main()
本身只是一个函数,因此将代码转换为独立函数相对简单。传统上,您会在调用函数中 open/validate 您的文件(调用 parsecolumns
函数的函数——在本例中为 main()
),然后简单地传递一个打开的 FILE*
文件流作为函数的参数。
下面显示了一个快速实现(对用户输入进行了合理验证 filename
)。始终,始终验证所有用户输入,并始终检查 EOF
以防用户通过使用 CTRL+D 或 [=70 生成手动 EOF
取消输入=]CTRL+Z(在 windoze 上)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* constants for max chars, start & stop values */
enum { BASE = 10, MAXC = 512, START = 770, STOP = 791 };
int parsecolumns (FILE *fp);
int main (void) {
char filename[MAXC] = "";
FILE *fp = NULL;
for (;;) { /* loop until good filename provided (or canceled) */
int rtn; /* always capture and validate scanf return */
printf ("enter filename: ");
if ((rtn = scanf ("%s", filename)) != 1) {
if (rtn == EOF) {
fprintf (stderr, " user canceled input.\n");
return 1;
}
}
else {
fp = fopen (filename, "r");
if (!fp) /* validate file open for reading */
fprintf (stderr, "error: file open failed. '%s'.\n", filename);
else
break;
}
}
if (parsecolumns (fp)) { /* parse columns in file */
fprintf (stderr, "error: failure in parsecolumns()\n");
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
int parsecolumns (FILE *fp)
{
char buf[MAXC] = ""; /* line buffer */
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: invalid file parameter 'fp'.\n");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
int val; /* 1st col value */
size_t len = strlen (buf); /* get buf len for validation */
char *p, *delim = " \t\n"; /* pointer & delimiters */
if (len && buf[len - 1] != '\n') { /* validate complete line */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXC);
return 1;
}
p = strtok (buf, delim); /* parse 1st column (as string) */
if (p == NULL) { /* handle 1st col error */
fprintf (stderr, "error: parse of first column failed.\n");
return 1;
}
errno = 0;
val = (int)strtol (p, NULL, BASE); /* convert to long/int */
if (errno) {
fprintf (stderr, "error: strtol() conversion failed.\n");
return 1;
}
/* for each line between START & STOP (inclusive) */
if (START <= val && val <= STOP) {
printf ("%d", val); /* output 1st col */
/* tokenize line, remove 2nd column, append 1 at end */
p = strtok (NULL, delim); /* parse/discard 2nd column */
if (!p) { /* handle failure to parse 2nd column */ }
p = strtok (NULL, delim); /* parse 3rd column */
if (!p) { /* handle failure to parse 3rd column */ }
while (p) { /* while token read */
printf (" %-8s", p); /* print token */
p = strtok (NULL, delim); /* parse next token/column */
}
printf ("1\n"); /* print final 1 at end */
}
}
return 0;
}
此程序的主要 objective:
打印第一列中的数字位于起始值和终止值之间的行,这取决于用户输入。但是开始和结束数字应该比较每行的每个第一个单词。
例如,
start = 770 stop = 791
它应该只比较每行的第一个整数。
删除每行的第二列并在每行的末尾添加(1 或任何数字),如示例输出格式。
请给我一些执行此操作的说明。
我尝试编写 C 代码,但没能成功。我附上它供您仔细阅读。
输入文件格式
766 102 1368 1369
767 102 1369 1370
768 102 1370 1371
769 102 1371 1372
770 102 1372 1373
771 102 1373 1374
772 102 1375 1376
773 102 1376 1377
774 102 1377 1378
775 102 1378 1379
776 102 1379 1380
777 102 1380 1381
778 102 1381 1382
779 102 1382 1383
780 102 1383 1384
781 102 1384 1385
782 102 1386 1387
783 102 1387 1388
784 102 1388 1389
785 102 1389 1390
786 102 1390 1391
787 102 1391 1392
788 102 1392 1393
789 102 1393 1394
790 102 1394 1395
791 102 1395 1396
717 204 339 1333 1341 1337
718 204 1337 1341 1342 1338
719 204 1338 1342 1335 355
720 204 1333 1334 1343 1341
721 204 1341 1343 1344 1342
722 204 1342 1344 1336 1335
输出格式
770 1372 1373 1
771 1373 1374 1
772 1375 1376 1
773 1376 1377 1
774 1377 1378 1
775 1378 1379 1
776 1379 1380 1
777 1380 1381 1
778 1381 1382 1
779 1382 1383 1
780 1383 1384 1
781 1384 1385 1
782 1386 1387 1
783 1387 1388 1
784 1388 1389 1
785 1389 1390 1
786 1390 1391 1
787 1391 1392 1
788 1392 1393 1
789 1393 1394 1
790 1394 1395 1
791 1395 1396 1
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE *file1;
FILE *out1;
///Function declaration
void OpenInputFile();
int Extract_SOLID_value( int To_Find_node, int To_Element_set_list);
int main()
{
int result;
OpenInputFile();
int ELEMENT_SOLID;
int STOP;
START = 10;
STOP = 100;
///Open txt file to write
out1 = fopen("output.txt", "w");
if (out1 == NULL)
{
perror("fopen");
exit(EXIT_FAILURE);
}
result = Extract_SOLID_value(START, STOP);
printf("%s", result);
fclose(file1);
fclose(out1);
return 0;
}/// end main
int Extract_SOLID_value( int To_Find_node, int To_Element_set_list)
{
char line[256];
int word;
if (!file1) {
perror("fopen");
exit(EXIT_FAILURE);
}
while (fscanf(file1,"%s%*[^\n]",word)){
if (word == START ){
fgets(line, sizeof (line),file1);
if (word == STOP){
return;
}
printf("%s \n", line);
fprintf(out1, "%s \n", line);
}
}
return 0;
}
void OpenInputFile(){
file1 = fopen("simple_CSDexample.dat", "r");
// check if file exists
if (!file1){
perror("fopen");
exit(EXIT_FAILURE);
}
return;
}
您已将 START
和 STOP
的值分别作为 To_Find_node
和 To_Element_set_list
赋给函数 Extract_SOLID_value()
,因此请在其中使用新名称该函数否则你会得到错误,因为 START
& STOP
不在该函数的范围内。
您检查了输入文件是否是在 OpenInputFile()
中创建的,所以为什么在 Extract_SOLID_value()
中的循环之前再次检查什么都没有改变?
当一行中的第一个值在所需范围内时,您需要继续扫描输入文件并将数据写入输出文件。
一个 fgets()
不行你必须依赖循环
int Extract_SOLID_value( int To_Find_node, int To_Element_set_list)
{
char line[256];
int word;
int fword;
while (fscanf(file1,"%d",&fword)==1){
if (fword >= To_Find_node && fword <=To_Element_set_list ){
fprintf(out1, "%d ", fword);
fprintf(stdout, "%d ", fword);
fscanf(file1, "%d", &word);
fgets(line, sizeof(line), file1);
line[strlen(line)-1]='[=10=]';
fprintf(out1, "%s 1\n", line);
fprintf(stdout, "%s 1\n", line);
}
else
{
fgets(line, sizeof(line), file1);
}
}
return 0;
}
fscanf()
将 return 成功分配给变量的数值。
只要在每次迭代开始时给 fword
赋值,循环就会继续下去。
因此 while
循环中的条件。
我们首先读取每行中的第一个数字,如果它在所需范围内,则将该数字与通过 fgets()
获得的该行的其余部分一起写入输出文件。
如果数字不在所需范围内,我们只是使用 fgets()
来跳过该行,但您也可以使用类似 fseek()
的内容。
fgets()
会将结尾的换行符 (\n
) 存储为已读入的字符串的一部分。
所以我们通过用 [=30=]
覆盖 \n
来标记字符串结尾来删除它。
您可以跳过带有 fprintf(stdout....)
的行。它相当于一个printf()
,我只是这样做是为了将写入文件的内容也打印到监视器上。
请记住,整数的格式说明符是 %d
而 不是 %s
.
有很多方法可以完成这个练习。除了基本的文件处理之外,主要目标是认识到您需要将列值处理为 integers 和 strings。 (第一列是唯一需要 整数 值的列)。
认识到您的比较需要一个 整数 值,您可以阅读整行,然后使用 sscanf
将第一列转换为 int
,或立即开始将每列 标记化 为字符串,并使用 strtol
将第一列转换为 int
。 (两种方式都可以)。
更大的问题是 标记 行。 (典型的选择是 strtok
——名副其实)。您也可以简单地使用一对指针和 "inch-worm" 从行的开始到结束,边走边挑选列。然而,对于简单的 space 或制表符分隔的列,strtok
使它变得容易。这是一种在一行中分隔未知数量的列的便捷方法,格式化输入函数将失败。
(注意:如果您需要保留具有 NULL 值的列,您还可以使用 strsep
函数——例如在您可能具有 col1,col2,,col4
的 .csv 文件中)
使用 strtok
和 strtol
执行到 int
的转换的实现可能如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* constants for max chars, start & stop values */
enum { BASE = 10, MAXC = 512, START = 770, STOP = 791 };
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* line buffer */
/* open file (or read from stdin -- default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
int val; /* 1st col value */
size_t len = strlen (buf); /* get buf len for validation */
char *p, *delim = " \t\n"; /* pointer & delimiters */
if (len && buf[len - 1] != '\n') { /* validate complete line */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXC);
return 1;
}
p = strtok (buf, delim); /* parse 1st column (as string) */
if (p == NULL) { /* handle 1st col error */
fprintf (stderr, "error: parse of first column failed.\n");
return 1;
}
errno = 0;
val = (int)strtol (p, NULL, BASE); /* convert to long/int */
if (errno) {
fprintf (stderr, "error: strtol() conversion failed.\n");
return 1;
}
/* for each line between START & STOP (inclusive) */
if (START <= val && val <= STOP) {
printf ("%d", val); /* output 1st col */
/* tokenize line, remove 2nd column, append 1 at end */
p = strtok (NULL, delim); /* parse/discard 2nd column */
if (!p) { /* handle failure to parse 2nd column */ }
p = strtok (NULL, delim); /* parse 3rd column */
if (!p) { /* handle failure to parse 3rd column */ }
while (p) { /* while token read */
printf (" %-8s", p); /* print token */
p = strtok (NULL, delim); /* parse next token/column */
}
printf ("1\n"); /* print final 1 at end */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
例子Use/Output
$ ./bin/parsecol <dat/columns.txt
770 1372 1373 1
771 1373 1374 1
772 1375 1376 1
773 1376 1377 1
774 1377 1378 1
775 1378 1379 1
776 1379 1380 1
777 1380 1381 1
778 1381 1382 1
779 1382 1383 1
780 1383 1384 1
781 1384 1385 1
782 1386 1387 1
783 1387 1388 1
784 1388 1389 1
785 1389 1390 1
786 1390 1391 1
787 1391 1392 1
788 1392 1393 1
789 1393 1394 1
790 1394 1395 1
791 1395 1396 1
使用 sscanf
将第一列转换为整数(并简单地将第一列重新读取为字符串)的第二个类似实现可能如下所示:
#include <stdio.h>
#include <string.h>
/* constants for max chars, start & stop values */
enum { MAXC = 512, START = 770, STOP = 791 };
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* line buffer */
/* open file (or read from stdin -- default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
int val; /* 1st col value */
size_t len = strlen (buf); /* get buf len for validation */
char *p, *delim = " \t\n"; /* pointer & delimiters */
if (len && buf[len - 1] != '\n') { /* validate complete line */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXC);
return 1;
}
if (sscanf (buf, "%d", &val) != 1) { /* convert 1st col to int */
fprintf (stderr, "error: conversion failure column 1.\n");
return 1;
}
/* for each line between START & STOP (inclusive) */
if (START <= val && val <= STOP) {
/* tokenize line, remove 2nd column, append 1 at end */
p = strtok (buf, delim); /* parse 1st column */
if (p == NULL) { /* handle 1st col error */
fprintf (stderr, "error: parse of first column failed.\n");
return 1;
}
printf ("%s", p); /* print column 1 */
p = strtok (NULL, delim); /* parse/discard 2nd column */
if (!p) { /* handle failure to parse 2nd column */ }
p = strtok (NULL, delim); /* parse 3rd column */
if (!p) { /* handle failure to parse 3rd column */ }
while (p) { /* while token read */
printf (" %-8s", p); /* print token */
p = strtok (NULL, delim); /* parse next token/column */
}
printf ("1\n"); /* print final 1 at end */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(注意:输出是一样的)
可以说,第一个更优雅一些,而在第二个中,您让 sscanf
处理转换和内部验证。
查看所有答案,如果您还有其他问题,请告诉我。
函数实现
针对您最后的评论,由于 main()
本身只是一个函数,因此将代码转换为独立函数相对简单。传统上,您会在调用函数中 open/validate 您的文件(调用 parsecolumns
函数的函数——在本例中为 main()
),然后简单地传递一个打开的 FILE*
文件流作为函数的参数。
下面显示了一个快速实现(对用户输入进行了合理验证 filename
)。始终,始终验证所有用户输入,并始终检查 EOF
以防用户通过使用 CTRL+D 或 [=70 生成手动 EOF
取消输入=]CTRL+Z(在 windoze 上)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* constants for max chars, start & stop values */
enum { BASE = 10, MAXC = 512, START = 770, STOP = 791 };
int parsecolumns (FILE *fp);
int main (void) {
char filename[MAXC] = "";
FILE *fp = NULL;
for (;;) { /* loop until good filename provided (or canceled) */
int rtn; /* always capture and validate scanf return */
printf ("enter filename: ");
if ((rtn = scanf ("%s", filename)) != 1) {
if (rtn == EOF) {
fprintf (stderr, " user canceled input.\n");
return 1;
}
}
else {
fp = fopen (filename, "r");
if (!fp) /* validate file open for reading */
fprintf (stderr, "error: file open failed. '%s'.\n", filename);
else
break;
}
}
if (parsecolumns (fp)) { /* parse columns in file */
fprintf (stderr, "error: failure in parsecolumns()\n");
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
int parsecolumns (FILE *fp)
{
char buf[MAXC] = ""; /* line buffer */
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: invalid file parameter 'fp'.\n");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
int val; /* 1st col value */
size_t len = strlen (buf); /* get buf len for validation */
char *p, *delim = " \t\n"; /* pointer & delimiters */
if (len && buf[len - 1] != '\n') { /* validate complete line */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXC);
return 1;
}
p = strtok (buf, delim); /* parse 1st column (as string) */
if (p == NULL) { /* handle 1st col error */
fprintf (stderr, "error: parse of first column failed.\n");
return 1;
}
errno = 0;
val = (int)strtol (p, NULL, BASE); /* convert to long/int */
if (errno) {
fprintf (stderr, "error: strtol() conversion failed.\n");
return 1;
}
/* for each line between START & STOP (inclusive) */
if (START <= val && val <= STOP) {
printf ("%d", val); /* output 1st col */
/* tokenize line, remove 2nd column, append 1 at end */
p = strtok (NULL, delim); /* parse/discard 2nd column */
if (!p) { /* handle failure to parse 2nd column */ }
p = strtok (NULL, delim); /* parse 3rd column */
if (!p) { /* handle failure to parse 3rd column */ }
while (p) { /* while token read */
printf (" %-8s", p); /* print token */
p = strtok (NULL, delim); /* parse next token/column */
}
printf ("1\n"); /* print final 1 at end */
}
}
return 0;
}