C程序读取文件读取额外的一行

我正在处理的代码涉及读取带有输入的文件,其结构如下:

(spaces)name(spaces) val (whatever) \n
(spaces)name(spaces) val (whatever) \n
(spaces)name(spaces) val (whatever) \n

其中空格表示任意数量的空白。我的代码应该给出名称和值。还有另一种情况,即忽略#号之后的行中的所有内容(就像注释一样)。输出应该是:

"name: (name) value: val \n" 

在大多数情况下,代码都在工作,除了它添加了额外的一行,它将在最后读取的数字上创建set name = null和val。例如我的测试文件:

a 12
b     33
#c 15
nice 6#9

输出为:

Line after:  a 12

name: a value: 12 :
Line after: b     33 

name: b  value: 33 :
Line after:  # c 15

Line after:  nice 6#9

name: nice value: 6 :
Line after:

name:  value: 6 : //why is this happening

代码在这里。

void readLine(char *filename)
{
    FILE *pf;
    char name[10000]; 
    char value[20];
    pf = fopen(filename,"r");
    char line[10000];
    if (pf){
        while (fgets(line,sizeof(line),pf) != NULL) {
            //printf("Line: %s\n",line);            
                printf("Line after: %s\n",line); 
                while(true){
                    int i=0;
                    char c=line[i]; //parse every char of the line
                            //assert(c==' ');
                            int locationS=0; //index in the name
                            int locationV=0; //index in the value
                            while((c==' ')&& i<sizeof(line)){
                                //look for next sequence of chars
                                ++i;
                                c=line[i];
                                if(c=='#'){
                                    break;
                                }
                            }
                            if(c=='#'){ break;}
                            assert(c!=' ');
                            while (c!=' '&&i<sizeof(line))
                            {
                                name[locationS]=c;
                                locationS++;
                                //printf("%d",locationS);
                                ++i;
                                c=line[i];if(c=='#'){
                                    break;
                                }
                            }
                            if(c=='#'){ break;}
                            assert(c==' ');
                            while(c==' '&&i<sizeof(line)){
                                //look for next sequence of chars
                                ++i;
                                c=line[i];
                                if(c=='#'){
                                    break;
                                }
                            }
                            if(c=='#'){ break;}
                            assert(c!=' ');
                            printf("\n");
                             while ((c!=' '&& c!='\n')&&i<sizeof(line))
                            {
                                value[locationV]=c;
                                locationV++;
                                ++i;
                                c=line[i];if(c=='#'){
                                    break;
                                }
                            }
                            printf("name: %s value: %s : \n",name,value);
                            memset(&name[0],sizeof(name));
                            memset(&value[0],sizeof(value));
                            break; //nothing interesting left
                }
        }
        fclose(pf);
    }else{
        printf("Error in file\n");
        exit(EXIT_FAILURE);
    }
}

mei96818968 回答:C程序读取文件读取额外的一行

Pasha,您正在正确地执行某些操作,但是随后您使尝试执行的操作变得更加困难。首先,避免在代码中使用魔术数字,例如char name[10000];。相反:

...
#define MAXC 1024   /* if you need a constant,#define one (or more) */

int main (int argc,char **argv) {

    char line[MAXC];
    ...

(您遵循不要在缓冲区大小上跳过规则 :)

同样,您在打开文件方面做得很好,并且在尝试使用fgets()进行读取之前,验证文件已打开以供读取。您可以在单个块中进行验证并在那时处理错误-这样可以减少整个代码其余部分的缩进程度,例如

    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1],"r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

现在,在打开文件并验证该文件已打开以供读取和处理任何错误的情况下,您可以继续读取文件中的每一行。除非您将名称存储在需要在读取循环中生存的数组中,否则您可以在读取循环块中简单声明name[MAXC];,例如

    while (fgets (line,MAXC,fp)) {    /* read each line of input */
        char name[MAXC];                /* storage for name */
        int val;                        /* integer value for val */

注意:,我们没有声明其他数组来保存值,而是简单地将val声明为int,并将使用sscanf来解析{{ 1}}和name那时将值直接转换为val

任何时候,只要您使用面向 line 的输入功能(例如int或POSIX fgets(),您都希望修剪getline()并包含在其中您可以使用'\n'轻松地做到这一点,请参见strspn(3) - Linux manual page。这是一个简单的单次调用,您可以将strcspn的返回值用作{的索引{1}},以便使用 nul-终止字符(即strcspn或简称'\n')覆盖'\n'

'\0'

现在您需要做的就是检查0中第一个 line[strcspn (line,"\n")] = 0; /* trim '\n' from end of line */ 的位置,该位置标志着注释的开始。如果找到,您将像使用'#'一样简单地用 nul-termination 字符覆盖line,例如

'#'

现在您已经排好了一行,并删除了'\n'和可能存在的任何注释,您可以检查 line[strcspn (line,"#")] = 0; /* overwrite '#' with nul-char */ 是否为空(这意味着它以'\n'开头或仅仅是只包含line的空行

'#'

注意: '\n'只是 if (!*line) /* if empty-string */ continue; /* get next line */ 的简写。当取消引用缓冲区时,例如if (!*line),您只是返回第一个元素(第一个字符)作为指针表示法中的if (line[0] == 0),与数组索引表示法中的*line等效。*line == *(line + 0)也用作解除引用。)

现在,使用*(line + 0) == line[0]直接从[]解析namevallinesscanf转换说明符都将忽略转换说明符之前的所有前导空格。只要"%s"本身不包含空格,就可以使用此简单方法。就像您 验证文件打开的退货 一样,您将 验证"%d"的退货 确定您指定的转化次数是否成功发生。例如:

name

注意:,通过对字符串使用 field-width 修饰符,例如sscanf,可以保护 if (sscanf (line,"%1023s %d",name,&val) == 2) /* have name/value? */ printf ("\nline: %s\nname: %s\nval : %d\n",line,val); else printf ("\nline: %s (doesn't contain name/value\n",line); 的数组边界。字段宽度限制了"%1023s"不能写多个name到名称,这不能由变量或宏提供,这是必须粘贴 magic-number 在您的代码中...对于每条规则,通常都有一个警告或两个警告...)

如果您要求进行两次转换,并且sscanf返回了1023 char + \0,则说明请求的两次转换均成功。此外,由于您为sscanf指定了整数转换,因此可以保证该值将包含整数

仅此而已。剩下的就是关闭文件(如果没有从2中读取),则操作完成。完整的示例可能是:

val

注意:,如果要在修剪stdin和注释之前打印原始#include <stdio.h> #include <string.h> #define MAXC 1024 /* if you need a constant,char **argv) { char line[MAXC]; /* use filename provided as 1st argument (stdin by default) */ FILE *fp = argc > 1 ? fopen (argv[1],"r") : stdin; if (!fp) { /* validate file open for reading */ perror ("file open failed"); return 1; } while (fgets (line,fp)) { /* read each line of input */ char name[MAXC]; /* storage for name */ int val; /* integer value for val */ line[strcspn (line,"\n")] = 0; /* trim '\n' from end of line */ line[strcspn (line,"#")] = 0; /* overwrite '#' with nul-char */ if (!*line) /* if empty-string */ continue; /* get next line */ if (sscanf (line,line); } if (fp != stdin) /* close file if not stdin */ fclose (fp); } ,只需在调用{ line。在'\n'上方打印,显示line在调用strcspn之前的最终状态)

使用/输出示例

使用存储在我系统上line中的输入文件,您只需执行以下操作即可读取从line重定向的值:

sscanf

注意:只需删除重定向dat/nameval.txt即可实际打开并从文件中读取文件,而不是让Shell为您完成。六对一,六对另一个。)

仔细检查一下,如果还有其他问题,请告诉我。如果由于某种原因您不能使用任何函数来帮助您解析该行并且必须仅使用指针或数组索引,请告诉我。按照上述方法,只需稍作努力即可将每个操作替换为其手动操作。

本文链接:https://www.f2er.com/3169303.html

大家都在问