我有一个txt文件,我只想获得大于12个字符的行。将这些行插入名为graph_node的图形变量中。
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-&gt; id和data-&gt; 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)
答案 0 :(得分:3)
您的代码中最大的问题是您经常忽略它
'\0'
- 终止字节并传递给strlen
等期望a的函数
有效字符串(以0结尾的字符串)。
例如insert_node
:
for(int i = 0; i < strlen(data->id); i++)
temp->id[i] = data->id[i];
在这里,您要复制期望'\0'
- 终止字节的所有字符,
temp->id
会存储一系列字符,但不是字符串。
strlen(data->id)
会有一个未定义的行为,因为data->id
最多
如果你初始化它而没有0终止字符串,则可能不会以0结尾。
如果您知道源字符串小于,请使用strcpy
49个字符长,或strncpy
完全确定:
strncpy(temp->id, data->id, sizeof temp->id);
temp->id[sizeof(temp->id) - 1] = 0;
与node_name
相同。您也不会检查malloc
是否返回
NULL
。
foef
行也是错误的,请参阅Why while(!foef(...))
is always wrong。
你应该重写这个
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-终止字节,结果是一个字符序列,但不是字符串。
下一行if(strlen(line) ...
因此而溢出,因为strlen
会
继续寻找超出限制的0终止字节。对我来说还不清楚
为什么你甚至检查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
,因为它允许您转换
不同基数的数字,从二进制到十六进制。第二个好处是
它告诉你它停止阅读的地方。这非常适合错误检测,
我使用这个behviour来检测线条是否具有正确的格式。如果
line具有正确的格式,数字旁边必须出现逗号,
,我检查
为此,如果不是这样,我打印一条错误消息并跳过该行
继续阅读文件。
下一个if
检查下一个字符是否为引用"
,因为根据您的
file,第二个参数用引号括起来。可悲的是,他的副手
arguments是一个逗号,也用作字符串中的普通字符。
这使事情变得更复杂(你不能在这里使用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
指向。{1}
第一个逗号,所以你必须跳过它。下一个字符是引用,所以你
也必须跳过它,因此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错误,那么这意味着 你仍未在代码的其他部分中出现错误 我们。