提问人:Januar Soepangat 提问时间:5/1/2023 更新时间:5/2/2023 访问量:72
在 C 中插入 2d 数组时,标记字符串被切断
Token string cuts off when inserting into 2d array in C
问:
我正确地标记了一行字符串中的单个单词;但是,将它们插入 2D 数组会切断部分令牌。我也有 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 或“\0”,但发生了相同的结果。在内部数组的 [N][0] 第 [N] 个索引中插入第一个单词是有效的,但是在插入到内部数组的其他索引中时,我缺少一些东西。
答:
比我上面的所有评论更糟糕的是,这可能会导致未定义的行为,是您用于打印单个字符的活动且始终 UB。%s
任务
arr[N][i] = *token;
将单个字符 分配给单个字符元素。token[0]
arr[N][i]
然后,你使用 来打印 ,但该格式需要一个你没有的以 null 结尾的字符串。%s
&arr[N][i]
%s
要有一个字符串,你需要一个字符数组,你可能应该做的是这样的(但我只是猜测):
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;
(为了清楚起见,我省略了几行。这从根本上是有缺陷的,因为可以返回 NULL(事实上,您期望它在某些时候返回),但是一旦为 NULL,尝试读取就是未定义的行为。编写此代码的标准惯用方法是:strtok
token
*token
while( (token = strtok(NULL, " \n")) != NULL ){ ... }
惯用代码有效。使用它。同样,在上一个循环中,不要只写 ,只需编写 对每次迭代进行 feof/ferror 检查是没有帮助的,并且代码看起来很不寻常。惯用代码乍一看可能看起来很奇怪,但它通常很容易理解,并且是构造循环的正确方法。while( !feof(...) && ! ferror(...) { if( fgets (...) ) ...}
while( fgets (....)) {...}
前面的回答已经涵盖了技术问题:
trim()
通常是指字符串开头和结尾的空格,但在这种情况下,您只需删除试用换行符即可。如 @Someprogrammerdude 所述,对于空字符串,它不能按预期工作。strtok()
当没有其他要处理的内容时,将返回 NULL。当您随后取消引用时,这就是导致段错误的原因。*token
adj[N][i]
在本例中是指 token 的第 i 个字符。这就是为什么您的输入不符合预期的原因您提到要构建邻接矩阵,但不是邻接矩阵,而是存储字符串的 2d 字符数组。
adj
我在这里为你写了相当多的代码来实现邻接矩阵。我不清楚你是否希望类依赖于它的要求或相反(这只是交换的问题,如果相反:course
prereq
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_node(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;
}
void adjacency_matrix_edge(adjacency_matrix *am, size_t a, size_t b) {
am->edges[am->nodes_len * a + b] = 1;
}
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);
int course;
for(int i = 0;; i++) {
char *token = strtok(i ? NULL : line, DELIM);
if(!token)
break;
if(!i) {
course = adjacency_matrix_node(am, token);
if(course == -1)
return EXIT_FAILURE;
continue;
}
int prereq = adjacency_matrix_node(am, token);
if(prereq == -1)
return EXIT_FAILURE;
adjacency_matrix_edge(am, course, prereq);
}
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 EXIT_FAILURE;
}
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
评论
trim
NULL
-1
fgets
*token
arr[N][i] = *token;
完全没有意义。请注意,不是第二个单词的开头,而是第一个单词的第二个字母。arr[N][1]