编写一个C程序,打开一个名为phoneList.txt的文本文件并搜索联系人(名字,姓氏,电话号码),并更新现有联系人的电话号码。我的问题出在更新电话号码程序中。当我使用fgets查找匹配的名称来更新联系人时,光标位于下一行的开头,也就是在与用户搜索匹配的联系人之后的联系开始时。 继承我的代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void){
struct record{
char firstName[20];
char lastName[20];
char phoneNum[15];
};
struct record info;
FILE *fp;
char line[100];
char phoneInfo[100];
char fullName[100];
char *stream;
int n = atoi(getenv("CONTENT_LENGTH"));
fgets(stream, n+1, stdin); //put query string into stream from stdin
//SET HTML OUTPUT AND TITLE, TEST CONTENT OF STREAM
printf("%s%c%c\n", "Content-type:text/html;charset=iso-8859-1",13,10);
printf("<p>%s</p>", stream);
sscanf(stream, "firstName=%[^&]&lastName=%[^&]&phoneNum=%s", info.firstName, info.lastName, info.phoneNum);
strcpy(fullName, info.firstName);
strcat(fullName, " ");
strcat(fullName, info.lastName);
strcpy(phoneInfo, fullName);
strcat(phoneInfo, " ");
strcat(phoneInfo, info.phoneNum);
//strcat(phoneInfo, "\n");
printf("%s", phoneInfo);
printf("\n");
//TEST FORMATTING OF PHONE INFO VAR
fp = fopen("phoneList.txt", "r+");
if(fp == NULL){
printf("Error opening the file.\n");
return 1;
}
while(fgets(line, 99, fp)!=NULL){
if(strstr(line, fullName)!= NULL){
//fseek(fp, -(strlen(phoneInfo)+1), SEEK_CUR);
fputs(phoneInfo, fp);
printf("Success! Number updated. \n");
fclose(fp);
return;
}
}
if(feof(fp)){
//fputs(phoneInfo, fp);
printf("goes to here");
}
fclose(fp);
return 0;
}
我有fseek评论,因为它的行为很奇怪,具体取决于被搜索的联系人是否在列表的末尾。我认为这与文本文件中有一个\ n字符有关。想知道是否有更好的方法来简单地覆盖与用户搜索匹配的行,或者至少将光标重置为与搜索匹配的行的开头。我已经做了一堆谷歌搜索和搜索这个网站,但我无法找到我理解如何实现的任何东西。真的很感谢你的帮助! 干杯
答案 0 :(得分:1)
要
我有fseek评论,因为它的行为很奇怪,具体取决于被搜索的联系人是否在列表的末尾。
fseek(fp, -(strlen(phoneInfo)+1), SEEK_CUR);
在最后一行,您应该返回strlen(phoneInfo),而不是strlen(phoneInfo)+1。
因为在其他行中,有一个换行符。但在最后一行,可能没有。您可以采用效率较低的方式对其进行修改:
if(line[strlen(line)-1]=="\n"){
fseek(fp, -(strlen(phoneInfo)+1), SEEK_CUR);
}
else{
fseek(fp, -strlen(phoneInfo), SEEK_CUR);
}
顺便说一句,如果phoneInfo的长度与txt文件中的相同,则代码只能正常工作。
答案 1 :(得分:0)
您必须在给定文件流的读取和写入之间执行定位操作。
fopen
函数¶7以更新模式打开文件时('
+
'作为第二个或第三个字符) 在上面的模式参数值列表中,输入和输出都可以在上面执行 相关流。但是,如果没有输入,输出不应直接输入 干预调用fflush
函数或文件定位函数(fseek
,fsetpos
或rewind
),输入不得直接跟随没有输出的输出 干预调用文件定位功能,除非输入操作遇到结束 文件。
您的代码必须执行某种fseek()
来重新定位文件以进行写入;在继续阅读更多之后,它还必须在写完之后再做fseek()
。您可能需要在阅读之前跟踪读取行的位置(ftell()
,fseek()
或fsetpos()
),以便您可以再次找到该行的开头。
另外,鉴于:
char line[100];
致电fgets()
:
while (fgets(line, 99, fp)!=NULL){
是安全的,但你可以同样安全地使用:
while (fgets(line, sizeof(line), fp) != NULL)
并使用您分配的所有空间,而不是仅仅99%。此外,如果将行长度从100更改为256或1024或4096,则只需更改声明 - 而不是更改使用的位置。
以类似的方式,你有:
struct record
{
char firstName[20];
char lastName[20];
char phoneNum[15];
};
struct record info;
FILE *fp;
char line[100];
char phoneInfo[100];
char fullName[100];
char *stream;
int n = atoi(getenv("CONTENT_LENGTH"));
fgets(stream, n+1, stdin); //put query string into stream from stdin
您没有为stream
分配空间来指向;你在你的课程中写作和随机的位置。
//SET HTML OUTPUT AND TITLE, TEST CONTENT OF STREAM
printf("%s%c%c\n", "Content-type:text/html;charset=iso-8859-1",13,10);
printf("<p>%s</p>", stream);
sscanf(stream, "firstName=%[^&]&lastName=%[^&]&phoneNum=%s",
info.firstName, info.lastName, info.phoneNum);
此sscanf()
应以各种方式升级:
if (sscanf(stream, "firstName=%19[^&]&lastName=%19[^&]&phoneNum=%14s",
info.firstName, info.lastName, info.phoneNum) != 3)
{
...sscanf failed...
}
这会对结构的字段强制执行正确的长度(请注意,您的变量使用20和15,但格式必须使用19和14 - 这个逐个也是溢出源),并检查转换都是成功的。
顺便说一句,如果新字符串和旧字符串的长度相同,那么在旧字母上写新电话号码是安全的。如果新字符串较短,则需要将其填充到旧的长度以删除旧数字的最后几位数字。如果新字符串更长,你就会遇到重大问题;你将覆盖下一个条目的开头。您必须按适当的字节数(旧条目和新条目之间的长度差异)随机播放文件的内容。有关可以将数据插入文件中间的代码,请参阅SO 10467711。
另请注意,文本文件的正式限制比二进制文件更多。然而,其中许多更理论而不是实际,特别是在Unix上。 I / O系统对文本文件执行CRLF到NL映射(这是Windows上的一个问题),这是约束的一个原因。