尝试包含行和列的文件 I/O,定位文件时出现问题 *

Attempting File I/O with lines and columns, problems with positioning the FILE *

我正在尝试编写 2 个函数:

出现了非常莫名其妙的错误,我已经修复好几个小时了,快崩溃了,请指出我的愚蠢。

观察到的行为:

  1. chknbuff() 正在跳过最后一个字符,所以我录制了一些可以解决该问题的代码。
  2. 阅读超过第一行(例如 ln=2,col=x)会在其他文件中触发 chkncpy() 中的 EOF 错误。
  3. 抑制 chknmove() 的 EOF 警告给出 Segmentation Faults ,这意味着确实遇到了 EOF

代码:

#include <stdio.h>
/*-------Function-Definitions------------*/
int chknmove(FILE *tomove,long long line,long long col){
// moves FILE * to line & col passed
    rewind(tomove); int chk,f; long long i,q; i=0; q=0; f=0;
    if(line>=1 && col>=1){
        while(i < line){
            chk = fgetc(tomove);
            if(chk == EOF){
                printf("\n Invalid Index.\n");f=-1;break;
            }
            else if(chk == '\n'){
                if(i == line - 1){
                    printf("\n Invalid Index.\n");f=-1;break;
                }
                i++; q = 0;
            }
            else if(i == line - 1 && q == col - 1){
                break;
            }
            else
                q++;
        }
        if(f==0)
            fseek(tomove,-1,SEEK_CUR);
    }
    else{
        printf("\n Invalid Index.\n");f=-1;
    }
    return f;
}
int chknbuff(FILE *source, char *dest, long long beginln, long long begincol, long long endln, long long endcol){
// copies file contents from beginning line&cols to end lines&cols to a string.
    int z = chknmove(source,beginln,begincol);
    if(z==0){
        int chk; long long i,q,m; i=0; q=0; m=0; //endcol++;
        if(endln>=1 && endcol>=1 && endln>=beginln){
            while(i < endln){
                chk = fgetc(source);
                if(chk == EOF){
                    printf("\n Invalid Index1.\n");z=-1;break;
                }
                else if(chk == '\n'){
                    if(i == endln-1){
                        printf("\n Invalid Index2.\n");z=-1;break;
                    }
                    dest[m]=chk;m++; i++; q = 0;
                }
                else if(i == endln-1 && q == endcol-1 ){
                    break;
                }
                else{
                    dest[m]=chk;m++; q++;
                }
            }
            if(z==0){
                fseek(source,-1,SEEK_CUR);
                chk = fgetc(source);
                if(chk!=EOF)
                    dest[m]=chk; m++;
            }
            dest[m++]='[=11=]';
        }
        else{
            printf("\n Invalid Index3.\n");z=-1;
        }
    }
    return z;
}
/*---------------------------------------------*/

int main(){
    FILE * write = fopen("xyz","w");
    if(write == NULL)
        perror("\n Error ");
    else{
        printf("\n Filename : "); char fpart[501]; scarf("%500[^\n]",fpart); FILE * part = fopen(fpart,"r");
        if(part==NULL)
            perror("\n Error ");
        else{
            long long beginln, begincol, endln, endcol; char sep1,sep2;
            printf("\n Starting Index : ");     scanf("%lld%c%lld",&beginln,&sep1,&begincol); eat();
            printf("\n Terminating Index : "); scanf("%lld%c%lld",&endln,&sep2,&endcol); eat();
            char buff[MAX_F_BUFF];int f = chknbuff(part,buff,beginln,begincol,endln,endcol);
            if(f==0)
                fputs(buff,write);  
        }
        int d = fclose(write);
        if(d==EOF)
            perror("\n Failed ");
        else
            printf("\n Success.\n"); 
    }
    return 0;
}

如果需要任何额外的信息,请评论,我会尽力添加

在这个 self-answer 中,我 post 我的方法为我解决了这个问题。我在问题中的功能有点拙劣和混乱,这里不再是。

我已经从 dxiv's answer 那里借来解决了它,并且我自己干预了如何处理 '\n' 字符。它工作得很好 afaik.

希望对以后的其他人有所帮助。

我需要的功能:


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;
    }
}

AND

int chknbuff(FILE* source, char *dest, long long beginln, long long begincol, long long endln, long long endcol) {
    /* Copies everything from FILE *source to char *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; long long m = 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){
                    dest[m]=ch; m++;
                    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 !
                    dest[m]=ch; m++;
                }
            }
            else if(i==endln && q==endcol){
                dest[m]=ch; m++; break;
            }

            else{
                q++; dest[m]=ch; m++;
            }
        }
    }
    else
        f=-1;

    dest[m]='[=11=]'; // null terminate the buffer 
    if(f==0)    return 0;
    else    return -1;
}

如有遗漏errors/details欢迎留言,我会尽快回复

以下是对我的评论“你可以用一个函数完成所有操作”的扩展,并通过一次文件传递完成。 range 中的字符用 putchar(chk); 打印,可以替换为 fputc(chk, write);dest[count] = chk; 以保存到文件或字符串缓冲区。

#include <stdio.h>
#include <stdlib.h>

// return number of characters in 'copy' range between (beginline, begincol) and (endline, endcol)
// line/col are 1-based and the 'copy' range is inclusive of both begin/end positions
long long copy_range(FILE* fyl, long long beginln, long long begincol, long long endln, long long endcol) {
    long long line, col, count; int chk;

    if (beginln < 1 || begincol < 1 || endln < beginln || (endln == beginln && endcol < begincol))
        return -1;

    // assume current file position is at the beginning, maybe 'rewind' if that's not guaranteed
    line = 1; col = 1; count = -1;

    // read the file sequentially
    // start saving characters once (beginline, begincol) is reached
    // break out of the loop after (endline, endcol) is reached
    do {
        // attempt to read character at position (line, col)
        chk = fgetc(fyl);

        // attempt failed, return error
        // check count to see whether it happened before or within the 'copy' range
        // check line to see if the entire line is past EOF vs. just the target column
        if (chk == EOF) {
            printf("\nInvalid %s %s.\n",
                (count < 0) ? "Beginning" : "Ending",
                (line == beginln) ? "Column" : "Line");
            break;
        }

        // found newline character
        if (chk == '\n') {
            // error if looking for (beginln, begincol) and newline found on beginln before begincol
            //    or if looking for (endln, endcol) and newline found on endln before endcol
            if (line == ((count < 0) ? beginln : endln)) {
                printf("\nInvalid %s Column.\n",
                    (count < 0) ? "Beginning" : "Ending");
                break;
            }

            // comment the following if *not* preserving newlines in 'copy' range
            if (count >= 0) {
                putchar(chk); count++;
            }

            // update position and move on
            line++; col = 1; continue;
        }

        if (line == beginln && col == begincol) {
            // enter the 'copy' range, start counting
            printf("--- begin ---\n"); count = 0;
        }
        if (count >= 0) {
            // save character if within the 'copy' range
            putchar(chk); count++;
        }
        if (line == endln && col == endcol) {
            // leave the 'copy' range, return the count
            // if saving to a string, add the nul-terminator here, but do not increment counter
            printf("\n---- end ----\n"); break;
        }
        col++;
    } while (line <= endln);

    return count;
}

int main() {
    char fpart[501];
    printf("\nEnter Filename:\n");
    if (scanf("%500[^\n]", fpart) != 1) return -1;;

    FILE* part = fopen(fpart, "r");
    if (part == NULL) return errno;

    long long beginln, begincol, endln, endcol;
    printf("\nEnter Starting Index:\n");
    if (scanf("%lld %lld", &beginln, &begincol) != 2) return -2;;
    printf("\nEnter Terminating Index:\n");
    if (scanf("%lld %lld", &endln, &endcol) != 2) return -3;

    return copy_range(part, beginln, begincol, endln, endcol) > 0;
}

[ EDIT ] 在评论之后,澄清意味着将换行符计算为前一行的最后一个位置,这是对上面的修改,它移动了 '\n' 在字符被处理后检查,并删除现在多余的换行符回显。

long long copy_range(FILE* fyl, long long beginln, long long begincol, long long endln, long long endcol) {
    long long line, col, count; int chk;

    if (beginln < 1 || begincol < 1 || endln < beginln || endcol < ((endln == beginln) ? begincol : 1))
        return -1;

    line = 1; col = 1; count = -1;

    do {
        chk = fgetc(fyl);

        if (chk == EOF) {
            printf("\nInvalid %s %s.\n",
                (count < 0) ? "Beginning" : "Ending",
                (line == beginln) ? "Column" : "Line");
            break;
        }

        if (line == beginln && col == begincol) {
            printf("--- begin ---\n"); count = 0;
        }
        if (count >= 0) {
            putchar(chk); count++;
        }
        if (line == endln && col == endcol) {
            printf("\n---- end ----\n"); break;
        }

        if (chk == '\n') {
            if (line == ((count < 0) ? beginln : endln)) {
                printf("\nInvalid %s Column.\n",
                    (count < 0) ? "Beginning" : "Ending");
                break;
            }
            line++; col = 1;
        }
        else
            col++;
    } while (line <= endln);

    return count;
}