在 C 中插入二维数组时令牌字符串被截断

问题描述 投票:0回答:3

我正确地标记了一行字符串中的单个单词;但是,将它们插入二维数组会切断标记的一部分。我也有 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] 个索引中插入第一个单词是可行的,但是当插入到内部数组的其他索引中时,我缺少了一些东西。

c string multidimensional-array strtok adjacency-matrix
3个回答
1
投票

比我上面的所有评论更糟糕,可能导致未定义的行为,是活跃的和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
被用作索引变量有点令人困惑。


1
投票

考虑线条:

  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 检查没有帮助,而且代码看起来不正常。惯用代码一开始可能看起来很奇怪,但它通常很好理解并且是构建循环的正确方法。


0
投票

前面的回答已经涵盖了技术问题:

  1. trim()
    通常是指字符串开头和结尾的空格,但在这种情况下,您只需删除试用换行符。正如@Someprogrammerdude 所指出的,对于空字符串,它无法按预期工作。

  2. strtok()
    将在没有其他要处理的情况下返回 NULL。当您随后取消引用
    *token
    时,这就是导致您的段错误的原因。

  3. adj[N][i]
    在这种情况下指的是令牌的第 i 个字符。这就是为什么您的输入不符合预期

  4. 你提到你想建立一个邻接矩阵,但

    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
© www.soinside.com 2019 - 2024. All rights reserved.