尝试“插入”或“添加”到文本文件-一个小问题

问题描述

提供了代码M.R.E。

症状是“ 简单”:

  1. 尝试运行hello\nhello\nhelloEOF并在程序中插入'_'@ ln=2col=1(或更确切地说是任何line>1 && col==1),奇怪地 ,但吃光了'\n',导致第2行与第1行-hello_hello\nhelloEOF而不是hello\n_hello\nhelloEOF融合了
  2. 尝试在文件hello\nhello\nhelloEOF的最后一行输入一个字符,并在'_ln=3中插入'col=5'(或者在任何字符{{1}处插入) }(以EOF结尾的行)会跳过该行的最后一个 actual 字符:col>1而不是hello\nhello\nhell_EOF
  3. 尝试在最后一行(EOF终止的行中的hello\nhello\nhell_oEOF)中使用第一个字符会导致抛出错误消息(col==1)。

就我的直觉而言,我认为处理线路终止(\n Invalid Index.'\n')及其计数方法以及循环计数器时存在缺陷,但是我我不明白。

想法/算法:

下面的代码可以真正说明一切:

EOF

如果有任何遗漏的细节或错误错误,请发表评论,我会予以纠正。

解决方法

您的代码太复杂了:

  • 复制文件开头的不同情况应合并到一个函数调用中,该函数可以复制数据,直到行col上列line的字节为止(不包括该字节)。

  • >
  • 然后从用户输入中复制。

  • 最后,复制其余的输入文件。

  • 该代码假定行和列的编号从1开始。这可能很明显,但是您应该指定它,因为它可能对每个人都不明显。

  • 该样式很难理解:您应该在关键字,;之后以及{之前在二进制运算符周围使用水平空间。在一行上填充多个语句也不是一个好主意。

这里是简化版。通常,大多数代码用于错误处理:

#include <stdio.h>
#include <string.h>

void flush_line(FILE *fp) { /* read the rest of the current line */
    int c;
    while ((c = getc(fp)) != '\n' && c != EOF)
        continue;
}

int copy_file(FILE *from,FILE *to) {
    int c;
    while ((c = getc(from)) != EOF) {
        if (putc(c,to) == EOF)
            return EOF;
    }
    return 0;
}

int copy_lines(FILE *from,int line1,int col1,FILE *to) {
    int c,line = 1,col = 1;
    for (;;) {
        if (line >= line1 || (line == line1 && col >= col1))
            break;
        if ((c = getc(from)) == EOF)
            break;
        if (putc(c,to) == EOF)
            return EOF;
        col++;
        if (c == '\n') {
            line++;
            col = 1;
        }
    }
    return 0;
}

int writer(FILE *write) {
    char in[501]; /* str that stores input line-by-line */
    char *p;

    printf("\n Terminate Input with \"/end/\".\n\n   Type below :\n\n");
    while (fgets(in,501,stdin)) {
        if ((p = strstr(in,"/end/")) != NULL) {
            /* if line has "/end/",all chars till /end/ are written to file and input loop breaks */
            int o = p - in;
            if (fprintf(write,"%.*s",o,in) < 0)
                return EOF;
            break;
        } else {
            /* writes line to file */
            if (fputs(in,write) < 0)
                return EOF;
        }
    }
    return 0;
}

int main() {
    /* main to add/insert to file @ given index */
    const char *temp_filename = "Temp.Ctt";
    char fadd[501];/* filename str */

    printf("\n Filename : ");
    if (scanf("%500[^\n]",fadd) != 1)
        return 1;
    flush_line(stdin);

    FILE *add = fopen(fadd,"r");
    if (add == NULL) {
        perror("\n Cannot open input file");
        return 1;
    }
    int line,col;
    /* read the index: 1 based line and column numbers */
    printf("\n Index : ");
    if (scanf("%d%*c%d",&line,&col) != 2) {
        fprintf(stderr,"invalid input\n");
        return 1;
    }
    flush_line(stdin);
    FILE *tmp = fopen(temp_filename,"w");
    if (tmp == NULL) {
        perror("\n Cannot create temporary file");
        fclose(add);
        return 1;
    }
    if (copy_lines(add,line,col,tmp)) {
        perror("\n Error copying beginning of file");
        fclose(add);
        fclose(tmp);
        remove(temp_filename);
        return 1;
    }
    if (writer(tmp)) {
        perror("\n Error writing user input");
        fclose(add);
        fclose(tmp);
        remove(temp_filename);
        return 1;
    }
    if (copy_file(add,tmp)) {
        perror("\n Error copying remaining file contents");
        fclose(add);
        fclose(tmp);
        remove(temp_filename);
        return 1;
    }
    fclose(add);
    if (fclose(tmp) < 0) {
        /* if closing tmp was unsuccessful,the file on disk may be corrupted/incomplete,so must be removed */
        perror("\n Error closing temporary file");
        remove(temp_filename);
        return 1;
    }
    if (rename(temp_filename,fadd) == 0) {
        printf("\n Success.\n");
        return 0;
    }
    /* on Windows & some other non-POSIX systems,file cannot be renamed to pre-existing filename,hence delete original */
    if (remove(fadd)) {
        perror("\n Cannot remove input file");
        remove(temp_filename);
        return 1;
    }        
    if (rename(temp_filename,fadd) == 0) {
        printf("\n Success.\n");
        return 1;
    }
    /* if rename still unsuccessful,try and copy contents */
    tmp = fopen(temp_filename,"r");
    if (tmp == NULL) {
        perror("\n Cannot re-open temporary file,output is in Temp.Ctt");
        return 1;
    }
    add = fopen(fadd,"w");
    if (add == NULL) {
        perror("\n Cannot re-open input file,output is in Temp.Ctt");
        fclose(tmp);
        return 1;
    }
    if (copy_file(tmp,add)) {
        perror("\n Error copying temporary file to input file");
        fclose(add);
        fclose(tmp);
        return 1;
    }
    fclose(tmp);
    if (fclose(add) < 0) {
        perror("\n Error closing input file");
        return 1;
    }
    /* throw an error,remove tmp file and give up */
    remove(temp_filename);
    printf("\n Success.\n");
    return 0;
}
,

这是我的自我解答chknmove()chkncpy()的问题导致了“ glitching ”,以下已解决。

下面的代码可以立即编译,并经过大量注释和隔开,因此实际上比封装在chknmove()chkncpy()中的主要思想大。 -因此这些已放置在代码的顶部。

希望这个问题/答案将来对其他人有用。

#include <stdio.h> // For file & console I/O
#include <string.h> // For strstr() in writer()

int chknmove(FILE *tomove,long long line,long long col){
    /* Moves FILE* to given ((line,col) -1 char),such that next char read from FILE* will be @ (line,col)
     Checks validity of index as it moves : if col not in line || EOF encountered,returns -1. */

    rewind(tomove); // rewind file 'just in case'

    int f = 0 ; // control variable which stores state (succeeded/failed) of chknmove()

    if (line < 1 || col < 1) 
    {
        f=-1; 
        printf("\n Illegal Index.\n");
        // Follows 1-based line/col index : -ve values are illegal
        return -1;
    }
    else {
        long long i,q; i = q = 0; // i = lines encountered ; q = chars encountered in line i ; both are 0-based

        while(i < line){
            int chk = fgetc(tomove); // 
            if(chk == EOF){
                printf("\nInvalid %s - beyond EOF.\n",(i == line -1 ) ? "Column" : "Line"); 
                f = -1; break;
            }
            else if(chk == '\n'){
                if(i==line-1 && q == col-1) 
                /*  1. This allows for user to directly point to the '\n' char,allowing him to append to line 
                    2.(line/col - 1) : since i & q are 0-based   */
                    break;
                else{
                    if(i == line-1 ){
                        // except if target index was the '\n',reading beyond newline @ target line is invalid,since '\n' terminates line
                        printf("\nInvalid column for line %lld.\n",line); 
                        f=-1; break;
                    }
                    i++; q=0; // if not @ target line,reset and continue 
                }
            }
            else if(i == line-1  && q == col-1 ) // if dest index reached,break .
                break;
            else // if non-EOF,non-\n char encountered,increment q and continue.
                q++;
        }

        if(f==0){
            fseek(tomove,-1,SEEK_CUR); // So that the after returning/exiting chknmove(),the char read from FILE* is @ line,col
            return 0;
        }
        else
            return -1;
    }
}
int chkncpy(FILE* source,FILE *dest,long long beginln,long long begincol,long long endln,long long endcol) {
    /* Copies everything from FILE *source to FILE *dest within begining index and terminating index,if they're valid
        Returns -1 if they're invalid.*/

    if (beginln < 1 || begincol < 1 || endln < beginln || endcol < ((endln == beginln) ? begincol : 1))
        // -ve indexes and reading/writing backwards is illegal
        return -1;

    long long i,q; // i -> lines && q -> chars 
    int f=0;
    if(chknmove(source,beginln,begincol)==0){ 
        // checked if begining index is valid and if so,moved to it.

        i=beginln; q=begincol; // i & q have same base as line & col,so 1-based

        while(1){
            int ch = fgetc(source);
            if(ch==EOF){
                printf("\nInvalid Terminating Index.\n");
                f=-1; break;
            }
            else if(ch=='\n'){
                if(i==endln && q==endcol){
                    fputc(ch,dest);
                    break;
                }
                else{
                    if(i==endln){
                        printf("Invalid column for line %lld.\n",endln);
                        f=-1; break;
                    }
                    i++; q=1; // q set to 1 -> q is 1-based !
                    fputc(ch,dest);
                }
            }
            else if(i==endln && q==endcol){
                fputc(ch,dest); break;
            }

            else{
                q++; fputc(ch,dest);
            }
        }
    }
    else
        f=-1;
    
    if(f==0)    return 0;
    else    return -1;
}
long long lcc(FILE *fyl,long long line){ 
    // L.C.C. == line char count,i.e,count of chars in a line (including the \n).

     int f = chknmove(fyl,1); /* attempt moving to line,store returned val */

    long long cnt=0; // cnt -> number of chars found 
    if(f==0){ // if line exists,then :
        while(1){
            int g = fgetc(fyl);
            if (g==EOF) // EOF checked in case line is last line
                break;
            else if(g=='\n'){ // '\n' is EOL,hence it is counted and then loop is terminated .
                cnt++; break;
            }
            else
                cnt++;
        }
        rewind(fyl);
        return cnt;
    }
    else
        return -1; // if line doesn't exist,return -1    
}
int clone(FILE *wfrm,FILE *wto){
    // clones wfrom ("Write From ") onto wto ("Write To") until EOF is encountered.

    while(1){
        int a =fgetc(wfrm);
        if(a==EOF)
            break;
        else
            fputc(a,wto);                    
    }

    return 0;
}
void writer(FILE *write){
    // Allows basic console line-level I/O for writing to FILE * 

    printf("\n Terminate Input with \"/end/\".\n\n\tType below :\n\n");
    char in[501]; /* str that stores input line-by-line */
    char *p; int o;
    while(1){
        fgets(in,stdin); /* takes line from user */
        if((p=strstr(in,"/end/"))!=0){ 
            /* if line has "/end/",all chars till /end/ are written to file and input loop breaks */
            o = p-in;
            fprintf(write,in);
            break;
        }
        else{
            /* writes line to file */
            fputs(in,write);
        }
    }
}
void eat() /* clears stdin */
{
    int eat;while ((eat = getchar()) != '\n' && eat != EOF);
}
int main(){
    /* main to add/insert to file @ given index */

    char fadd[501]=""; // String to store fname
    printf("\n Filename : "); scanf("%500[^\n]",fadd); eat(); // Take fname,clear stdin .
    FILE * add = fopen(fadd,"r"); // open file

    if(add==NULL)
        perror("\n Error "); 
    else{ 
        // If File is loaded for reading successfully

        long long line,col; char sep; // line,col and seperating char make up the index
        printf("\n Index : "); scanf("%lld%c%lld",&sep,&col); eat(); // take index,clear stdin

        FILE * tmp=fopen("Temp.Ctt","w"); // open a temporary file
        if(tmp==NULL)
            perror("\n Error ");
        else{
            int f;
            if(line>=1 && col>1){ // copy till the line,col-1
                f = chkncpy(add,tmp,1,col-1);
            }
            else if(line>1 && col==1){ // copy till line-1,last char  
                f = chkncpy(add,line-1,lcc(add,line-1));
            }
            else if(line==1 && col==1){ // no moving/copying necessary at all 
                f=0;
            }
            else{
                printf("\n Invalid Index.\n");f=-1;
            }
            if(f==0){ // if Index was not invalid 

                writer(tmp); // let user write to temp file 
                clone(add,tmp); //clones the rest of add to tmp - both are *not* fclosed
                int ok = fclose(tmp); fclose(add);
                if(ok==EOF){
                    /* if closing tmp was unsuccessful,so must be removed */
                    remove("Temp.Ctt");perror("\n Error ");

                }
                else{
                    if(rename("Temp.Ctt",fadd)==0)
                        printf("\n Success.\n");
                    else{
                        /* on Windows & some other non-POSIX systems,hence delete original */
                        remove(fadd);
                        if(rename("Temp.Ctt",fadd)==0)
                            printf("\n Success.\n");
                        else{
                            /* if rename still unsuccessful,throw an error,point user to temp file and give up */
                            perror("\n Error ");
                            printf("\n %s lost. File-buffer exists as %s in cwd.\n",fadd,"Temp.Ctt");
                        }  
                    }
                }

            }
            else
                remove("Temp.Ctt");
        }
    }
    return 0;
}

欢迎提出任何可能改善答案的建设性批评。如果有任何意外错误或缺少详细信息,请发表评论,我会尽快答复。