我正确地标记了一行字符串中的单个单词;但是,将它们插入二维数组会切断标记的一部分。我也有 NULL 的问题,代码导致段错误。
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // strtok
#define MAX_FILE_LENGTH 30
#define MAX_COURSE_LENGTH 30
#define MAX_LINE_LENGTH 1000
void trim(char* str) {
int l = strlen(str);
if (str[l - 1] == '\n') {
str[l - 1] = 0;
}
}
int main() {
char filename[MAX_FILE_LENGTH];
char arr[MAX_COURSE_LENGTH][MAX_COURSE_LENGTH];
const char delim[] = " ";
char* token;
int course = 0;
char c;
FILE* fp;
int N = 0; // number of lines in file
printf("This program will read, from a file, a list of courses and their prerequisites and will print the list in which to take courses.\n");
printf("Enter filename: ");
scanf("%s%c", filename, &c);
fp = fopen(filename, "r");
if (fp == NULL) {
printf("Could not open file %s. Exit\n", filename);
printf("\nFailed to read from file. Program will terminate.\n");
return -1;
}
while (!feof(fp) && !ferror(fp)) {
int i = 0;
if (fgets(arr[N], MAX_LINE_LENGTH, fp)) {
trim(arr[N]);
printf("Full line: |%s|\n", arr[N]);
token = strtok(arr[N], delim);
arr[N][i] = *token;
printf("N = %d, i = %d, token = %s arr[%d][%d]: %s\n", N, i, token, N, i, &arr[N][i]);
while (token != NULL) {
i++;
token = strtok(NULL, " \n");
printf("token at arr[%d][%i]: %s value at arr[%d][%d]: %s\n", N, i, token, N, i, &arr[N][i]);
arr[N][i] = *token;
printf("N = %d, i = %d, token = %s arr[%d][%d]: %s\n", N, i, token, N, i, &arr[N][i]);
}
N++;
}
}
fclose(fp);
return 0;
}
我得到的输出是:
Full line: |c100 c200|
N = 0, i = 0, token = c100 arr[0][0]: c100
token at arr[0][1]: c200 value at arr[0][1]: 100
N = 0, i = 1, token = c200 arr[0][1]: c00
token at arr[0][2]: (null) value at arr[0][2]: 00
zsh: segmentation fault ./a.out
我的文件是一个课程列表,我要用先修课程列表构建一个邻接矩阵。
c100 c200
c300 c200 c100
c200 c100
我尝试在插入标记之前将每个索引重置为 NULL 或 ' ',但出现了相同的结果。在内部数组的第 [N][0] 个索引中插入第一个单词是可行的,但是当插入到内部数组的其他索引中时,我缺少了一些东西。
比我上面的所有评论更糟糕,可能导致未定义的行为,是活跃的和always你使用
%s
打印单个字符的UB。
作业
arr[N][i] = *token;
将token[0]
中的
单个字符赋值给单个字符元素
arr[N][i]
.
然后您使用
%s
来打印 &arr[N][i]
,但是 %s
格式需要一个空终止的 string,而您没有。
要有一个字符串,你需要一个字符数组,而你也许应该做的是(但我只是猜测):
char arr[MAX_COURSE_LENGTH][MAX_COURSE_LENGTH][MAX_COURSE_LENGTH];
和
strcpy(arr[N][i], token);
正如 Allan Wind 所提到的,您忘记了
strotk
可以(并且将会)返回一个 NULL
指针。
另请注意,所有大写名称通常用于宏和符号常量。这使得看到名称
N
被用作索引变量有点令人困惑。
考虑线条:
while (token != NULL) {
token = strtok(NULL, " \n");
arr[N][i] = *token;
(为了清楚起见,我省略了几行。)这是根本性的缺陷,因为
strtok
可以返回 NULL(实际上,您期望它在某个时候返回),但是一旦 token
为 NULL,尝试read *token
是未定义的行为。编写此代码的标准惯用方式是:
while( (token = strtok(NULL, " \n")) != NULL ){ ... }
惯用代码有效。用它。同样在前面的循环中,不用
while( !feof(...) && ! ferror(...) { if( fgets (...) ) ...}
,只写while( fgets (....)) {...}
每次迭代都做feof/ferror 检查没有帮助,而且代码看起来不正常。惯用代码一开始可能看起来很奇怪,但它通常很好理解并且是构建循环的正确方法。
前面的回答已经涵盖了技术问题:
trim()
通常是指字符串开头和结尾的空格,但在这种情况下,您只需删除试用换行符。正如@Someprogrammerdude 所指出的,对于空字符串,它无法按预期工作。
strtok()
将在没有其他要处理的情况下返回 NULL。当您随后取消引用*token
时,这就是导致您的段错误的原因。
adj[N][i]
在这种情况下指的是令牌的第 i 个字符。这就是为什么您的输入不符合预期
你提到你想建立一个邻接矩阵,但
adj
是一个二维字符数组,你在其中存储字符串。
我在这里为您编写了相当多的代码来实现邻接矩阵。我不清楚你是想让班级取决于它的要求还是相反(这只是在
course
中交换 token
和 process()
的问题,如果它是相反的:
#include <stdio.h>
#include <stdlib.h>
#define POSIX_C_SOURCE 200809L
#include <string.h>
#define DELIM " "
#define MAX_FILE_LENGTH 30
#define MAX_COURSE_LENGTH 30
#define MAX_LINE_LENGTH 1000
typedef struct adjacency_matrix {
size_t nodes_len;
char **nodes;
char *edges;
} adjacency_matrix;
struct adjacency_matrix *adjacency_matrix_create(size_t nodes_len) {
adjacency_matrix *am = malloc(sizeof *am);
if(!am)
return NULL;
am->nodes_len = nodes_len;
am->nodes = calloc(nodes_len, sizeof *am->nodes);
am->edges = calloc(nodes_len * nodes_len, 1);
return am;
}
int adjacency_matrix_add(adjacency_matrix *am, char *node) {
int i = 0;
for(; am->nodes[i]; i++)
if(!strcmp(am->nodes[i], node))
return i;
if(i == am->nodes_len)
return -1;
am->nodes[i] = strdup(node);
return i;
}
int adjacency_matrix_edge(adjacency_matrix *am, char *a, char *b) {
int ai = adjacency_matrix_add(am, a);
if(ai == -1)
return EXIT_FAILURE;
int bi = adjacency_matrix_add(am, b);
if(bi == -1)
return EXIT_FAILURE;
am->edges[am->nodes_len * ai + bi] = 1;
return EXIT_SUCCESS;
}
void adjacency_matrix_print(const adjacency_matrix *am) {
for(size_t i = 0; am->nodes[i]; i++) {
printf("%s:", am->nodes[i]);
for(size_t j = 0; am->nodes[j]; j++) {
if(am->edges[i * am->nodes_len + j])
printf(" %s", am->nodes[j]);
}
printf("\n");
}
}
void adjacency_matrix_free(adjacency_matrix *am) {
if(!am)
return;
free(am->nodes);
free(am->edges);
free(am);
}
void trim(char *str) {
str[strcspn(str, "\n")] = '\0';
}
int process(adjacency_matrix *am, char *line) {
trim(line);
char *course;
for(int i = 0;; i++) {
char *token = strtok(i ? NULL : line, DELIM);
if(!token)
break;
if(!i) {
course = token;
continue;
}
if(adjacency_matrix_edge(am, course, token) == EXIT_FAILURE)
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int main() {
printf("This program will read, from a file, a list of courses and their prerequisites and will print the list in which to take courses.\n");
printf("Enter filename: ");
char filename[MAX_FILE_LENGTH];
scanf("%s", filename);
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
printf("Could not open file %s. Exit\n", filename);
printf("\nFailed to read from file. Program will terminate.\n");
return -1;
}
adjacency_matrix *am = adjacency_matrix_create(MAX_COURSE_LENGTH);
for(;;) {
char line[MAX_LINE_LENGTH];
if (!fgets(line, MAX_LINE_LENGTH, fp))
break;
process(am, line);
}
fclose(fp);
adjacency_matrix_print(am);
adjacency_matrix_free(am);
}
这是相应的输出:
c100: c200
c200: c100
c300: c100 c200