我有一个txt文件,我只想得到超过12个字符的行。然后将这些行插入类型为graph_node的称为graph的变量中。
txt文件:
1,"execCode(workStation,root)","OR",0
2,"RULE 4 (Trojan horse installation)","AND",0
3,"accessFile(workStation,write,'/usr/local/share')","OR",0
4,"RULE 16 (NFS semantics)","AND",0
5,"accessFile(fileServer,write,'/export')","OR",0
6,"RULE 10 (execCode implies file access)","AND",0
7,"canAccessFile(fileServer,root,write,'/export')","LEAF",1
6,7,-1图形节点类型:
#ifndef Graph_Structure
#define Graph_Structure
struct Graph_Node{
char id[50];
char node_name[50];
struct Graph_Node* next;
struct Graph_Node* edges;
};
typedef struct Graph_Node graph_node;
#endif 这是将数据插入图形变量的方法:
void insert_node(graph_node** node, graph_node* data){
printf("\nINSERTING\n");
graph_node* temp = (graph_node*)malloc(sizeof(graph_node));
for(int i = 0; i < strlen(data->id); i++)
temp->id[i] = data->id[i];
for(int i = 0; i < strlen(data->node_name) - 1; i++)
temp->node_name[i] = data->node_name[i];
temp -> next = *node;
*node = temp;
}下面是获取txt文件中超过12个字符的行的方法:
void generate_nodes(graph_node** graph, char* file_name){
graph_node* data = (graph_node*)malloc(sizeof(graph_node));
FILE* f_Data_Pointer = fopen(file_name, "r");
FILE* f_Aux_Pointer = fopen(file_name, "r");
char c = 0; char line[256];
int counter = 0;
int q = 0; //quotation marks
bool jump_line = false;
while(!feof(f_Data_Pointer)){
c = 0; memset(line, 0, sizeof(line));
while(c != '\n' && !feof(f_Aux_Pointer)){ // check line
c = fgetc(f_Aux_Pointer);
line[counter] = c;
counter++;
}
if(strlen(line) > 12){ //lines with no edges
/*line[counter-3] != '-' && line[counter-2] != '1'*/
int size = strlen(line); printf("\nline size: %d\n", size);
counter = 0; c = 0;
while(c != ','){ //id
c = fgetc(f_Data_Pointer);
if(c != ','){
data->id[counter] = c;
counter++;
}
printf("%c", c);
}
counter = 0; c = 0;
while(1){ //node_name
c = fgetc(f_Data_Pointer);
if(c != '"'){
data->node_name[counter] = c;
counter++;
}
else{
q++;
}
if(q > 1 && c == ',')
break;
printf("%c", c);
}
counter = 0; c = 0;
while(c != '\n'){
c = fgetc(f_Data_Pointer);
printf("%c", c);
}
insert_node(graph, data);
memset(data->id, 0, sizeof(data->id));
memset(data->node_name, 0, sizeof(data->node_name));
}
else{ //lines with edges
while(c != '\n' && !feof(f_Data_Pointer)){
c = fgetc(f_Data_Pointer);
}
}
}
fclose(f_Data_Pointer);
fclose(f_Aux_Pointer);
}我在"strlen“中的"for”命令的insert方法中得到了错误,它说data->id和data->node_name没有初始化,但我不明白为什么。我在数据上使用了malloc:
graph_node* data = (graph_node*)malloc(sizeof(graph_node));错误:
Conditional jump or move depends on uninitialised value(s) ==3612== at 0x4C30B18: strcpy (vg_replace_strmem.c:510) ==3612== by 0x4008B2: insert_node (mulval.c:44) ==3612== by 0x400C03: generate_nodes (mulval.c:159) ==3612== by 0x400CE8: main (mulval.c:187)发布于 2018-02-14 05:49:08
代码中最大的问题是,您经常忽略'\0'-terminating字节,并传递给像strlen这样的函数,它需要一个有效的字符串(以0结尾的字符串)。
例如在insert_node中
for(int i = 0; i < strlen(data->id); i++)
temp->id[i] = data->id[i];这里你正在复制除字符串以外的所有字符,temp->id将存储一个字符序列,但它是a string。strlen(data->id)将具有未定义的行为,因为如果您在初始化data->id时没有对字符串进行0结尾,那么它很可能不会以0结尾。
在这里,如果您知道源字符串的长度小于49个字符,则应使用strcpy,或者使用strncpy以确保完全确定:
strncpy(temp->id, data->id, sizeof temp->id);
temp->id[sizeof(temp->id) - 1] = 0;node_name也是如此。此外,您也没有检查malloc是否返回了NULL。
此外,请参阅Why while(!foef(...)) is always wrong,foef线路也是错误的。
您应该重写此代码
while(c != '\n' && !feof(f_Aux_Pointer)){ // check line
c = fgetc(f_Aux_Pointer);
line[counter] = c;
counter++;
}如下所示:
int c; // <-- not char c
while((c = fgetc(f_Aux_Pointer)) != '\n' && c != EOF)
line[counter++] = c;
line[counter] = 0; // terminating the string!fgetc返回一个int,而不是char,它应该是int,否则与EOF的比较会出错。在这里,您忽略了设置0终止字节,结果是一个字符序列,而不是一个字符串。由于strlen将继续查找超出限制的0终止字节,因此下一行if(strlen(line) ...溢出。对于我来说,甚至不清楚为什么要检查EOF,因为当外部while循环恢复时,您会继续阅读。如果f_Aux_Pointer到了结尾,函数不应该直接返回吗?我不太清楚你在那里做什么。此外,对于换行符不在前49个已读字符中,也没有相应的策略。我认为你应该重新考虑你的策略。
这样做也会更容易:
if(fgets(line, sizeof line, f_Aux_Pointer) == NULL)
{
// error handling
return; // perhaps this?
}
line[strcspn(line, "\n")] = 0; // removing the newline
if(strlen(line) > 12)
...这里
while(c != ','){ //id
c = fgetc(f_Data_Pointer);
if(c != ','){
data->id[counter] = c;
counter++;
}
printf("%c", c);
}您有与上面相同的错误,您从未设置\0-终止字节。在while的末尾,您应该有data->id[counter] = 0;。在下一个while循环中也是如此。
在generate_nodes中,您不需要为临时graph_node对象分配内存,因为insert_node无论如何都会创建一个副本。您可以执行以下操作:
graph_node data;
...
data.id[counter] = c;
...
data.node_name[counter] = c;
...
insert_node(graph, &data);
data.id[0] = 0;
data.node_name[0] = 0;这样你就少了一个malloc的麻烦了。
编辑
因为您的ID始终是数字的,所以我将结构更改为:
struct Graph_Node{
int id;
char node_name[50];
struct Graph_Node* next;
struct Graph_Node* edges;
}; 这会让生活变得更轻松。如果您的条件是只有您需要的行长于12个字符,并且您感兴趣的行始终与答案中发布的行的格式相同(逗号之间没有空格,第二列始终是引号),那么您可以这样解析它:
int generate_nodes(graph_node **graph, const char *file_name)
{
if(file_name == NULL || graph == NULL)
return 0; // return error
// no need to allocate memory for it
// if the insert_node is going to make a
// copy anyway
struct Graph_Node data = { .next = NULL, .edges = NULL };
FILE *fp = fopen(file_name, "r");
if(fp == NULL)
{
fprintf(stderr, "Error opening file %s: %s\n", file_name,
strerror(errno));
return 0;
}
// no line will be longer than 1024
// based on your conditions
char line[1024];
size_t linenmr = 0;
while(fgets(line, sizeof line, fp))
{
linenmr++;
// getting rid of the newline
line[strcspn(line, "\n")] = 0;
if(strlen(line) <= 12)
continue; // resume reading, skipt the line
char *sep;
long int id = strtol(line, &sep, 0);
// assuming that there is not white spaces
// before and after the commas
if(*sep != ',')
{
fprintf(stderr, "Warning, line %lu is malformatted, '<id>,' exepcted\n", linenmr);
continue;
}
data.id = id;
// format is: "....",
if(sep[1] != '"')
{
fprintf(stderr, "Warning, line %lu is malformatted, \"<string>\", exepcted\n", linenmr);
continue;
}
// looking for ",
char *endname = strstr(sep + 2, "\",");
if(endname == NULL)
{
fprintf(stderr, "Warning, line %lu is malformatted, \"<string>\", exepcted\n", linenmr);
continue;
}
// ending string at ",
// easier to do strcnpy
*endname = 0;
strncpy(data.node_name, sep + 2, sizeof data.node_name);
data.node_name[sizeof(data.node_name) - 1] = 0;
insert_node(graph, &data);
}
fclose(fp);
return 1;
}现在有趣的部分是:
char *sep;
long int id = strtol(line, &sep, 0);
// assuming that there is not white spaces
// before and after the commas
if(*sep != ',')
{
fprintf(stderr, "Warning, line %lu is malformatted, '<id>,' exepcted\n", linenmr);
continue;
}strtol是一个将字符串中的数字转换为实际整数的函数。此函数比atoi更好,因为它允许您将不同基数的数字从二进制转换为十六进制。第二个优点是它会告诉你它在哪里停止阅读。这对于错误检测非常有用,我使用此行为来检测该行是否具有正确的格式。如果行的格式正确,则必须在数字旁边显示逗号,,如果不正确,则打印一条错误消息并跳过该行,继续读取文件。
下一个if检查下一个字符是否是引号",因为根据您的文件,第二个参数包含在引号中。遗憾的是,参数的分隔符是逗号,它也用作字符串中的普通字符。这使得事情变得有点复杂(这里不能使用strtok )。再一次,我假设整个争论都以",结束。请注意,这不会考虑转义引号。像这样的一行:
3,"somefunc(a,b,\"hello\",d)","OR",0会被错误地解析。当找到",时,我将引号设置为'\0',以便更容易地使用strncpy,否则我将不得不计算字符串的长度,检查它的长度是否没有超过目标大小,等等。如果您需要继续解析,endname + 1应该指向下一个逗号,否则格式错误。
strncpy(data.node_name, sep + 2, sizeof data.node_name);
data.node_name[sizeof(data.node_name) - 1] = 0;在这里,我复制字符串。源是sep + 2,因为sep指向第一个逗号,所以您必须跳过它。下一个字符是引号,因此您也必须跳过它,因此使用sep + 2。因为名称的长度是未知的,所以最好使用strncpy,这将确保您不会写入超过所需的字节。
最后一步是将节点插入到图中。请注意,我没有分配内存,因为我知道insert_node无论如何都会创建一个新的副本。由于data仅供临时使用,因此不需要分配内存,只需将指向data的指针(通过传递&data)传递给insert_node即可。
我已经用一个虚拟graph和一个仅打印节点的虚拟insert_node测试了这个函数:
void insert_node(graph_node** node, graph_node* data)
{
printf("ID: %d, node_name: %s\n", data->id, data->node_name);
}这是输出:
ID: 1, node_name: execCode(workStation,root)
ID: 2, node_name: RULE 4 (Trojan horse installation)
ID: 3, node_name: accessFile(workStation,write,'/usr/local/share')
ID: 4, node_name: RULE 16 (NFS semantics)
ID: 5, node_name: accessFile(fileServer,write,'/export')
ID: 6, node_name: RULE 10 (execCode implies file access)
ID: 7, node_name: canAccessFile(fileServer,root,write,'/export')现在,如果你使用这段代码,并且不断得到valgrind错误,那么这意味着你在代码的其他部分仍然有错误,你还没有给我们展示。
https://stackoverflow.com/questions/48775429
复制相似问题