尝试将 'insert' 或 'add' 写入文本文件 - 一个小问题
Attempting to 'insert' or 'add' into a text file - one small problem
已提供代码 M.R.E。
症状是“简单”:
- 好奇地尝试
hello\nhello\nhelloEOF
并插入“_
”@ ln=2
& col=1
(或者任何 line>1 && col==1
)程序 做对了,但吃掉了 '\n'
,导致第 2 行 与第 1 行 融合 hello_hello\nhelloEOF
,而不是 hello\n_hello\nhelloEOF
.
- 尝试在文件
hello\nhello\nhelloEOF
最后一行的字符上插入 '_
' @ ln=3
& col=5
(或者更确切地说在任何字符 col>1
在由 EOF 终止的行中)导致它跳过该行的最后一个 actual 字符:hello\nhello\nhell_EOF
而不是 hello\nhello\nhell_oEOF
.
- 尝试使用最后一行中的第一个字符(
col==1
以 EOF 终止的行)导致抛出错误消息(\n Invalid Index.
)。
就我的直觉而言,我认为处理线路终止('\n'
、EOF
)及其计数方式以及一般循环计数器时存在缺陷,但是我没听懂。
idea/algorithm :
- 从用户处获取源文件
- 从用户获取索引(行,列)
- 从源复制到 tmp 文件直到(用户索引 -1 列)。
- 现在,允许用户将字符串写入 tmp 文件的缓冲区。
- 一旦用户终止输入,从用户索引复制到 tmp 直到
EOF
。
- 重命名/删除+重命名 tmp 作为用户源文件的名称。
下面的代码完成了所有真正的对话:
#include <stdio.h>
#include <string.h>
/* Likely error region in chknmove(), chkncpy() and/or main() ;
All other functions used are also provided */
long long lncnt(FILE *lcount){
/* counts the number of lines in a file with fgets() */
rewind(lcount); /* ensures lines counted from beginning */
long long lines=0; char line[501];
while((fgets(line,501,lcount))!=NULL){
lines++;
}
rewind(lcount); /* leaves fptr @ 0,0 rather than EOF */
return lines;
}
int chknmove(FILE *tomove, long long line, long long col){
/* Function to check & move FILE* to certain line/col */
rewind(tomove); /* make sure fptr is at beginning of file */
int f=0; /* set check-variable to 0 for successful */
if(line<1 || col<1)
f = -1; /* Ln,col cannot be -ve or even 0 -> 1st col is @ 1ln,1col in any non-empty file */
else{
long long i,q; i=1;q=0; /* i = lines seen, q = cols seen */
/* i init to 1 for ease of comprehension v/s testing 'line-1' */
while(i<line || q<col){ /* loop must iterate until i==line && q==col */
int ch = fgetc(tomove); /* int to store EOF */
if(ch==EOF){
f=-1; break; /* if file ends before reaching index, failed. */
}
else if(ch=='\n') {
if(i==line){
f=-1; break;/* if line ends before reaching col in ln, failed. */
}
i++; q=0; /* else , increase i by 1 and set q (col) to 0 */
}
else
q++; /* any other character is 'normal' , just increment q (col) by 1 */
}
}
if(f==0){
fseek(tomove,-1,SEEK_CUR);
/* since index must be checked, loop reaches it -> fseek(-1) causes *next* r/w to fptr to be on the index . */
return 0;
}
else{
/* f != 0 : moving has failed. */
printf("\n Invalid Index.\n");
return -1;
}
}
int chkncpy(FILE *source, FILE *dest,long long beginln, long long begincol, long long endln, long long endcol){
rewind(source); int f=0;
if(beginln<1 || begincol<1 || endln<beginln || endcol<1 || (beginln==endln && endcol<begincol))
f=-1; /* line/col must be >= 1 in non-empty file, copying done top to bottom (begin-index <= end-index ) */
else{
if(chknmove(source,beginln,begincol)==0){
/* loop begins if begin-index is valid */
long long i,q; i=beginln;q=begincol; /* i = lines, q = cols */
while(i<endln || q<endcol){
/* while end-index is not reached : !(i==endln && q==endcol) */
int ch = fgetc(source);
if(ch==EOF) {f=-1; break;} /* File ends b/w begin-index & end-index - failed. */
else if(ch=='\n') {
if(i==endln) {f=-1; break;} /* endln ends before reaching endcol - failed */
i++; q=0;
fputc(ch,dest); /* valid '\n' put in dest file */
}
else{
q++; fputc(ch,dest); /* valid char put in dest file */
}
}
}
else f=-1; /* if begin-index is invalid, failed. */
}
if(f==-1){
/* if f != 0 : chkncpy() failed */
printf("\n Invalid Index.\n");
return -1;
}
else /* if f remains == 0 */ return 0;
}
long long lastlncol(FILE *fyl){
rewind(fyl); /* makes sure fptr @ beginning */
chknmove(fyl,lncnt(fyl),1); /* move to last line */
long long cnt=0; /* cnt stores no. of chars in last line */
while(1){
int g = fgetc(fyl);
if (g==EOF)
break; /* EOF not counted */
else if(g=='\n'){
cnt++;break; /* \n is counted as a char, but also as EOL */
}
else
cnt++; /* all other chars counted */
}
rewind(fyl); /* leaves file at its beginning */
return cnt;
}
long long lcc(FILE *fyl,long long line){
rewind(fyl); int f = chknmove(fyl,line,1); /* moves fptr to line */
long long cnt=0;
if(f==0){
while(1){
int g = fgetc(fyl);
if (g==EOF)
break;
else if(g=='\n'){
cnt++;break;
}
else
cnt++;
}
rewind(fyl);
return cnt;
}
else
return -1;
}
void writer(FILE *write, int ctrl){
printf("\n Terminate Input with \"/end/\".\n\n Type below :\n\n");
char in[501]; /* str that stores input line-by-line */
char *p; int o;
while(1){
fgets(in,501,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,"%.*s",o,in);
break;
}
else{
/* writes line to file */
fputs(in,write);
}
}
if(ctrl==0){
/* in some cases, file must not be closed yet , hence ctrl is taken */
int s = fclose(write);
if(s==EOF) printf("\n Error ");
else printf("\n Success.\n");
}
}
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];/* filename str */
printf("\n Filename : "); scanf("%500[^\n]",fadd);
FILE * add = fopen(fadd,"r");
if(add==NULL)
perror("\n Error "); /* if file does not pre-exist */
else{
long long line, col; char sep; printf("\n Index : "); scanf("%lld%c%lld",&line,&sep,&col); /* takes index in ln-char-col form */
eat(); /* clears stdin */
FILE * tmp=fopen("Temp.Ctt","w"); /* opens a tmp file to write */
if(tmp==NULL)
perror("\n Error "); /* failed - tmp file could not be created to write */
else{
int f; /* success var */
if(line==1 && col>1){
/* if user wants to insert @ a col>1 in line 1 */
f = chkncpy(add,tmp,1,1,line,col); /* all below calls intend to write till 1 char before the char @ given index */
}
else if(line>1 && col>1){
/* if user wants to insert @ a col>1 in any line>1*/
f = chkncpy(add,tmp,1,1,line,col-1);
}
else if(line>1 && col==1){ //ERRORS - ignores the '\n' of line-1
/* if user wants to insert @ a 1st col in line>1 */
f = chkncpy(add,tmp,1,1,line-1,lcc(add,line-1));
}
else if(line==1 && col==1){
/* if user wants to insert @ 1,1 (no moving/copying needed) */
f=0;
}
else{
printf("\n Invalid Index.\n");f=-1;
}
if(f==0){
writer(tmp,1);
/* calls function to allow user to write to fptr , with ctrl != 0, so writer() *won't* fclose(tmp) */
chkncpy(add,tmp,line,col,lncnt(add),lastlncol(add));
/* copies all characters from index till EOF */
int ok = fclose(tmp); fclose(add);
if(ok==EOF){
/* if closing tmp was unsuccessful, the file on disk may be corrupted/incomplete, 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, file cannot be renamed to pre-existing filename , hence delete original */
remove(fadd);
if(rename("Temp.Ctt",fadd)==0)
printf("\n Success.\n");
else{
/* if rename still unsuccessful, throw an error, remove tmp file and give up */
remove("Temp.Ctt");
perror("\n Error ");
}
}
}
}
}
}
}
如有遗漏或不慎错误,请评论,我会改正。
你的代码太复杂了:
复制文件开头的不同情况应合并到一个函数调用中,该函数调用复制数据直到并排除第 col
行的字节 line
.
然后你从用户输入复制。
最后复制输入文件的其余部分。
代码假定行和列的编号从 1
开始。这可能很明显,但您应该指定它,因为它可能对每个人来说都不明显。
风格很难读:你应该在二元运算符周围使用水平space,在关键字,
和;
之后以及[=16之前=].在一行中塞满多个语句也不是一个好主意。
这是一个简化版本。通常,大部分代码用于错误处理:
#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;
}
这是我的 self-answer,chknmove()
和 chkncpy()
的问题导致“故障 ',这些已在下面修复。
下面是 ready-to-compile ,并且慷慨地注释和间隔,因此实际上比封装在 chknmove()
中的 主要思想 更大chkncpy()
- 因此这些已放在代码的顶部。
希望这个question/answer对以后的其他人有用。
#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,line,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,501,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,"%.*s",o,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",&line,&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,1,line,col-1);
}
else if(line>1 && col==1){ // copy till line-1 , last char
f = chkncpy(add,tmp,1,1,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, the file on disk may be corrupted/incomplete, 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, file cannot be renamed to pre-existing filename , 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;
}
欢迎任何可能改进答案的建设性批评。如有任何不慎错误或遗漏细节,请发表评论,我会尽快回复。
已提供代码 M.R.E。
症状是“简单”:
- 好奇地尝试
hello\nhello\nhelloEOF
并插入“_
”@ln=2
&col=1
(或者任何line>1 && col==1
)程序 做对了,但吃掉了'\n'
,导致第 2 行 与第 1 行 融合hello_hello\nhelloEOF
,而不是hello\n_hello\nhelloEOF
. - 尝试在文件
hello\nhello\nhelloEOF
最后一行的字符上插入 '_
' @ln=3
&col=5
(或者更确切地说在任何字符col>1
在由 EOF 终止的行中)导致它跳过该行的最后一个 actual 字符:hello\nhello\nhell_EOF
而不是hello\nhello\nhell_oEOF
. - 尝试使用最后一行中的第一个字符(
col==1
以 EOF 终止的行)导致抛出错误消息(\n Invalid Index.
)。
就我的直觉而言,我认为处理线路终止('\n'
、EOF
)及其计数方式以及一般循环计数器时存在缺陷,但是我没听懂。
idea/algorithm :
- 从用户处获取源文件
- 从用户获取索引(行,列)
- 从源复制到 tmp 文件直到(用户索引 -1 列)。
- 现在,允许用户将字符串写入 tmp 文件的缓冲区。
- 一旦用户终止输入,从用户索引复制到 tmp 直到
EOF
。 - 重命名/删除+重命名 tmp 作为用户源文件的名称。
下面的代码完成了所有真正的对话:
#include <stdio.h>
#include <string.h>
/* Likely error region in chknmove(), chkncpy() and/or main() ;
All other functions used are also provided */
long long lncnt(FILE *lcount){
/* counts the number of lines in a file with fgets() */
rewind(lcount); /* ensures lines counted from beginning */
long long lines=0; char line[501];
while((fgets(line,501,lcount))!=NULL){
lines++;
}
rewind(lcount); /* leaves fptr @ 0,0 rather than EOF */
return lines;
}
int chknmove(FILE *tomove, long long line, long long col){
/* Function to check & move FILE* to certain line/col */
rewind(tomove); /* make sure fptr is at beginning of file */
int f=0; /* set check-variable to 0 for successful */
if(line<1 || col<1)
f = -1; /* Ln,col cannot be -ve or even 0 -> 1st col is @ 1ln,1col in any non-empty file */
else{
long long i,q; i=1;q=0; /* i = lines seen, q = cols seen */
/* i init to 1 for ease of comprehension v/s testing 'line-1' */
while(i<line || q<col){ /* loop must iterate until i==line && q==col */
int ch = fgetc(tomove); /* int to store EOF */
if(ch==EOF){
f=-1; break; /* if file ends before reaching index, failed. */
}
else if(ch=='\n') {
if(i==line){
f=-1; break;/* if line ends before reaching col in ln, failed. */
}
i++; q=0; /* else , increase i by 1 and set q (col) to 0 */
}
else
q++; /* any other character is 'normal' , just increment q (col) by 1 */
}
}
if(f==0){
fseek(tomove,-1,SEEK_CUR);
/* since index must be checked, loop reaches it -> fseek(-1) causes *next* r/w to fptr to be on the index . */
return 0;
}
else{
/* f != 0 : moving has failed. */
printf("\n Invalid Index.\n");
return -1;
}
}
int chkncpy(FILE *source, FILE *dest,long long beginln, long long begincol, long long endln, long long endcol){
rewind(source); int f=0;
if(beginln<1 || begincol<1 || endln<beginln || endcol<1 || (beginln==endln && endcol<begincol))
f=-1; /* line/col must be >= 1 in non-empty file, copying done top to bottom (begin-index <= end-index ) */
else{
if(chknmove(source,beginln,begincol)==0){
/* loop begins if begin-index is valid */
long long i,q; i=beginln;q=begincol; /* i = lines, q = cols */
while(i<endln || q<endcol){
/* while end-index is not reached : !(i==endln && q==endcol) */
int ch = fgetc(source);
if(ch==EOF) {f=-1; break;} /* File ends b/w begin-index & end-index - failed. */
else if(ch=='\n') {
if(i==endln) {f=-1; break;} /* endln ends before reaching endcol - failed */
i++; q=0;
fputc(ch,dest); /* valid '\n' put in dest file */
}
else{
q++; fputc(ch,dest); /* valid char put in dest file */
}
}
}
else f=-1; /* if begin-index is invalid, failed. */
}
if(f==-1){
/* if f != 0 : chkncpy() failed */
printf("\n Invalid Index.\n");
return -1;
}
else /* if f remains == 0 */ return 0;
}
long long lastlncol(FILE *fyl){
rewind(fyl); /* makes sure fptr @ beginning */
chknmove(fyl,lncnt(fyl),1); /* move to last line */
long long cnt=0; /* cnt stores no. of chars in last line */
while(1){
int g = fgetc(fyl);
if (g==EOF)
break; /* EOF not counted */
else if(g=='\n'){
cnt++;break; /* \n is counted as a char, but also as EOL */
}
else
cnt++; /* all other chars counted */
}
rewind(fyl); /* leaves file at its beginning */
return cnt;
}
long long lcc(FILE *fyl,long long line){
rewind(fyl); int f = chknmove(fyl,line,1); /* moves fptr to line */
long long cnt=0;
if(f==0){
while(1){
int g = fgetc(fyl);
if (g==EOF)
break;
else if(g=='\n'){
cnt++;break;
}
else
cnt++;
}
rewind(fyl);
return cnt;
}
else
return -1;
}
void writer(FILE *write, int ctrl){
printf("\n Terminate Input with \"/end/\".\n\n Type below :\n\n");
char in[501]; /* str that stores input line-by-line */
char *p; int o;
while(1){
fgets(in,501,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,"%.*s",o,in);
break;
}
else{
/* writes line to file */
fputs(in,write);
}
}
if(ctrl==0){
/* in some cases, file must not be closed yet , hence ctrl is taken */
int s = fclose(write);
if(s==EOF) printf("\n Error ");
else printf("\n Success.\n");
}
}
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];/* filename str */
printf("\n Filename : "); scanf("%500[^\n]",fadd);
FILE * add = fopen(fadd,"r");
if(add==NULL)
perror("\n Error "); /* if file does not pre-exist */
else{
long long line, col; char sep; printf("\n Index : "); scanf("%lld%c%lld",&line,&sep,&col); /* takes index in ln-char-col form */
eat(); /* clears stdin */
FILE * tmp=fopen("Temp.Ctt","w"); /* opens a tmp file to write */
if(tmp==NULL)
perror("\n Error "); /* failed - tmp file could not be created to write */
else{
int f; /* success var */
if(line==1 && col>1){
/* if user wants to insert @ a col>1 in line 1 */
f = chkncpy(add,tmp,1,1,line,col); /* all below calls intend to write till 1 char before the char @ given index */
}
else if(line>1 && col>1){
/* if user wants to insert @ a col>1 in any line>1*/
f = chkncpy(add,tmp,1,1,line,col-1);
}
else if(line>1 && col==1){ //ERRORS - ignores the '\n' of line-1
/* if user wants to insert @ a 1st col in line>1 */
f = chkncpy(add,tmp,1,1,line-1,lcc(add,line-1));
}
else if(line==1 && col==1){
/* if user wants to insert @ 1,1 (no moving/copying needed) */
f=0;
}
else{
printf("\n Invalid Index.\n");f=-1;
}
if(f==0){
writer(tmp,1);
/* calls function to allow user to write to fptr , with ctrl != 0, so writer() *won't* fclose(tmp) */
chkncpy(add,tmp,line,col,lncnt(add),lastlncol(add));
/* copies all characters from index till EOF */
int ok = fclose(tmp); fclose(add);
if(ok==EOF){
/* if closing tmp was unsuccessful, the file on disk may be corrupted/incomplete, 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, file cannot be renamed to pre-existing filename , hence delete original */
remove(fadd);
if(rename("Temp.Ctt",fadd)==0)
printf("\n Success.\n");
else{
/* if rename still unsuccessful, throw an error, remove tmp file and give up */
remove("Temp.Ctt");
perror("\n Error ");
}
}
}
}
}
}
}
如有遗漏或不慎错误,请评论,我会改正。
你的代码太复杂了:
复制文件开头的不同情况应合并到一个函数调用中,该函数调用复制数据直到并排除第
col
行的字节line
.然后你从用户输入复制。
最后复制输入文件的其余部分。
代码假定行和列的编号从
1
开始。这可能很明显,但您应该指定它,因为它可能对每个人来说都不明显。风格很难读:你应该在二元运算符周围使用水平space,在关键字
,
和;
之后以及[=16之前=].在一行中塞满多个语句也不是一个好主意。
这是一个简化版本。通常,大部分代码用于错误处理:
#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;
}
这是我的 self-answer,chknmove()
和 chkncpy()
的问题导致“故障 ',这些已在下面修复。
下面是 ready-to-compile ,并且慷慨地注释和间隔,因此实际上比封装在 chknmove()
中的 主要思想 更大chkncpy()
- 因此这些已放在代码的顶部。
希望这个question/answer对以后的其他人有用。
#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,line,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,501,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,"%.*s",o,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",&line,&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,1,line,col-1);
}
else if(line>1 && col==1){ // copy till line-1 , last char
f = chkncpy(add,tmp,1,1,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, the file on disk may be corrupted/incomplete, 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, file cannot be renamed to pre-existing filename , 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;
}
欢迎任何可能改进答案的建设性批评。如有任何不慎错误或遗漏细节,请发表评论,我会尽快回复。