繁体   English   中英

添加文件字符到二维动态数组C编程

[英]Adding file characters to 2d dynamic array C programming

我有一个任务,我们从输入文件中获取内容并将其放入数组中,第一行的数字代表数组的维度。 前 6 个关于文本文件的前 6 行/行,我已经将其放入一维射线中,但之后的字符我必须以已解决的填字游戏的形式将它们放入 5x6 二维数组中(带有空格包括并确保有一个额外的列用于 '\0'so 5x7) 我必须使用 malloc 或 calloc 来制作数组,我的问题是制作矩阵,因为它没有空格或者我得到分段转储。 我首先尝试制作 arrays(两个 arrays - W 字符串的一维数组和字符 NxM 的二维数组)并确保元素在矩阵中,但就像我说的那样,我一直在获取分段转储。 我不知道该怎么做。 txt文件:

6 5 6
nail
taco
name
men
next
can
 next 
namect
  e aa
  n nc
 nail

到目前为止,这是我的代码:

void freeMatrix(char **matrix, int r, int c);

int main(int argc, char **argv)
{
    int W, N, M;
    char inBuf[SIZE], words[SIZE], crosswords[SIZE];
    char *word;
    char *outp;
    FILE *input;
    // FILE *output;

    if (argc != 2)
    {
        printf("Error, please input the filenames\n");
        exit(-1);
    }

    if ((input = fopen(argv[1], "r")) == NULL)
    {
        printf("Error, the file %s cannot be opened\n", argv[1]);
    }
    fgets(inBuf, SIZE, input);
    sscanf(inBuf, "%i%i%i", &W, &N, &M);
    word = (char *)malloc(sizeof(char) * W);
    char *crossword[N];
    char *ptr = word;
    char token[N][M];

    for (int x = 0; x < W; x++)
    {
        fgets(inBuf, SIZE, input);
        sscanf(inBuf, "%s", &words);
        printf("%s\n", words);
        *ptr = *words;
    }

    for (int f = 0; f < N; f++)
    {
        crossword[f] = (char *)malloc(N * sizeof(char));
    }
    for (int y = 0; y < N; y++)
    {
        for (int z = 0; z < M; z++)
        {
            fscanf(input, "%c", &token[y][z]);
            crossword[y][z] = token[y][z];
            printf("%s", crossword[y][z]);
        }
        printf("\n");
    }

    fclose(input);
    free(word);
    freeMatrix(crossword, N, M);
    return 0;
}

void freeMatrix(char **matric, int r, int c)
{
    for (int i = 0; i < r; i++)
    {

        free(matric[i]);
    }

    free(matric);
}

我希望用矩阵中的各个字符打印一个填字游戏,就像文本文件设置它的方式一样。 导致分段错误。 它将前 6 个单词打印为字符串数组,但二维字符数组是我不足的地方。

指甲

炸玉米饼

名称

男人

下一个

能够

分段错误(核心已转储)

这里有很多错误。 我的第一个建议是先让简单的部分工作。 将 6 个单词读入一个数组,然后打印出来。 先让它工作。 (目前在您的代码中不起作用,您不保存单词然后在任何地方输入)。 这是目前出了什么问题

fgets(inBuf, SIZE, input); // ok
sscanf(inBuf, "%i%i%i", &W, &N, &M); //ok

下面这个词的目的是什么? 它是一个包含 6 个字符的数组。 这似乎没有意义,因为 6 是单词的数量,而不是单词的长度

word = (char *)malloc(sizeof(char) * W);
char *crossword[N];
char *ptr = word; 
//  char token[N][M]; <<<< dont need this yet

// ok this loop read W words from the input file.
for (int x = 0; x < W; x++)
{
    fgets(inBuf, SIZE, input); // good
    sscanf(inBuf, "%s", &words); // you could have fscanf directly but ok
    printf("%s\n", words); // print it
    *ptr = *words;  // meaningless line
}

我认为最后一行以某种方式试图将您刚刚读到的单词复制到缓冲区“word”中,因为您之前做过*ptr = word

首先注意奇怪的名字,我希望“单词”是数组或病房,“单词”是您刚刚阅读的那个。

更重要的是,这不是你复制字符串的方式

所以让我们解决这个问题

首先我们需要一个指向单词的指针数组

 char *words[N];

我不愿意输入它,因为它不是标准的 c,但你在任何地方都使用它(可变长度 arrays,我的编译器根本不允许它,即使我问得很好)。 但我会做的。

现在我们需要一个字符缓冲区来将当前单词读入

 char word[SIZE]; // get rid of old word and words

现在让我们做一些阅读

 // get rid of old word and words
 char word[SIZE]; 
 char *words[N]; 

for (int x = 0; x < W; x++)
{
    fgets(inBuf, SIZE, input);
    sscanf(inBuf, "%s", &word);
    words[x] = strdup(word);
}

现在

for (int x = 0; x < W; x++)
{
      printf("%s\n", words[x];
}

证明我们确实有数据

在继续下一节之前完成所有工作

PS,strdup 是一个很棒的 function,它为字符串分配正确的大小,然后将字符串复制到 malloced 缓冲区,返回一个指向新 mallocd 的指针 memory

该程序中存在几个常见错误。


如果fopen失败,您会打印一条警告,但仍会继续执行该程序。 您应该在此处退出,或以其他方式处理该事件以防止将来读取失败。

将错误打印到标准错误 stream 也是明智的。

if ((input = fopen(argv[1], "r")) == NULL)
{
    /* printf("Error, the file %s cannot be opened\n", argv[1]); */
    fprintf(sterr, "Failed to open [%s].\n", argv[1]);
    return EXIT_FAILURE;
}

在 C 中malloc (或calloc等)的结果。可以安全地隐式地将void *提升为任何其他指针类型。

除此之外, sizeof (char)保证为1

/* word = (char *)malloc(sizeof(char) * W); */
word = malloc(W);

sscanf(inBuf, "%s", &words)中, &words是一个char (*)[SIZE] ,这是%s的错误类型。

不需要& words的类型为char [SIZE] ,当传递给 function 时,它将衰减为适当的char *类型。


fgetsfscanfsscanf都可能失败。 您应该防范这种情况,以防止程序的 rest 使用不正确的数据。

fgets在失败时返回NULL *scanf函数返回执行的成功转换次数( "%s"是一次转换, "%i%i%i"是三次)。

if (NULL == fgets(inBuf, SIZE, input))
    /* handle this event */;
    
    
if (1 != sscanf(inBuf, "%s", words))
    /* handle this event */;

*ptr = *words; 不执行字符串复制。 实际上,它将words的第一个元素复制到ptr的第一个元素。

ptr = words将分配指针值。

=从不用于字符串复制,而是使用strcpy function 完成复制:

char one[64];
char two[] = "hello world."
strcpy(one, two);

必须注意确保目标缓冲区有足够的空间用于要复制的字符串(包括其 null 终止字节)。


crossword[y][z]是一个char 使用%c而不是%s来打印单个字符。

printf(/* "%s" */ "%c", crossword[y][z]);

scanf说明符%s跳过前导空格,开始将字符读入其缓冲区,并在遇到更多空格时停止。

sscanf(" e aa", "%s", buffer)的结果会使buffer等同于"e" 您将无法以这种方式解析其中包含空格的行。

字符集转换说明符 ( %[] ) 可用于克服此限制,因为它允许读取空格。 %[^\n]将读取所有字符,直到遇到换行符。

请注意,使用不带限制字段宽度说明符%s%[]gets一样是不安全的,因为您可能会溢出缓冲区。

char buffer[16];
if (1 == sscanf("  e aa\n", "%15[^\n]", buffer))
    printf("[%s]", buffer); /* => [  e aa] */

话虽如此,简单的解决方案是从输入缓冲区中删除换行符(将其替换为 null 终止字节),如果它存在,并直接使用该缓冲区。

char input[] = "  e aa\n";
input[strcspn(input, "\n")] = 0;

crossword的元素初始化为

crossword[f] = (char *)malloc(N * sizeof(char));

N作为第二个维度的长度。 然后他们被访问为

for (int z = 0; z < M; z++) {
    /* ... */
    crossword[y][z] = token[y][z];

其中z依赖于不同的边界( M而不是N )。 如果N5M6 ,这将越界访问 memory 。


通常,不需要所有这些不同的中间缓冲区(和指向缓冲区的指针)。 这可以通过读取每一行的单个缓冲区来完成。

这是一个工作示例,其中包含一些辅助函数以减少代码重复。 该程序的大部分在run function 中。跟随它应该相对简单:

  1. 阅读header信息
  2. 为我们指向字符串的word_count指针分配足够的空间。
    1. 读取word_count行,每行:
      1. 为它分配足够的 memory
      2. 将字符串复制到这个分配的 memory
  3. rows * cols矩阵分配足够的 memory
    1. 读取行行,对于每个
      1. 将字符串中的每个字节(不包括 null 字节)复制到矩阵中正确的行和列
  4. 使用数据(显示)
  5. 释放所有分配

请注意,输入文件必须在需要填充到指定cols长度的行上包含尾随空格(如果过大则会截断)。

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* output formatted message to stderr and exit program unsuccessfully */
static void panic(const char *fmt, ...) {
    va_list args;

    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    fputc('\n', stderr);
    va_end(args);

    exit(EXIT_FAILURE);
}

/* read a line of input into a size limited buffer; removes newline character */
static size_t read_file_line(char *buffer, int size, FILE *file) {
    if (!fgets(buffer, size, file))
        panic("Could not read user input.");

    size_t length = strcspn(buffer, "\n");

    buffer[length] = 0;

    return length;
}

/* panic unless memory allocation successful */
static void *palloc(size_t bytes) {
    void *p = malloc(bytes);

    if (!p)
        panic("Could not allocate %zu bytes.", bytes);

    return p;
}

static void print_matrix(size_t r, size_t c, char (*m)[r][c]) {
    putchar('+');
    for (size_t i = 0; i < c; i++)
        putchar('-');
    puts("+");

    for (size_t i = 0; i < r; i++) {
        putchar('|');
        for (size_t j = 0; j < c; j++)
            putchar((*m)[i][j]);
        puts("|");
    }

    putchar('+');
    for (size_t i = 0; i < c; i++)
        putchar('-');
    puts("+");
}

static void run(FILE *file) {
    size_t word_count;
    size_t rows;
    size_t cols;

    char input[128];

    /* read our header information */
    read_file_line(input, sizeof input, file);

    if (3 != sscanf(input, "%zu%zu%zu", &word_count, &rows, &cols))
        panic("Could not read file header.");

    /* allocate the word list, read and allocate each word */
    char **words = palloc(sizeof *words * word_count);

    for (size_t i = 0; i < word_count; i++) {
        size_t len = read_file_line(input, sizeof input, file);

        words[i] = palloc(len + 1);
        strcpy(words[i], input);
    }

    /* allocate and read our matrix */
    char (*matrix)[rows][cols] = palloc(sizeof *matrix);

    for (size_t i = 0; i < rows; i++) {
        size_t len = read_file_line(input, sizeof input, file);

        if (len < cols)
            panic("Insufficient column data: Required %zu, Read %zu", cols, len);

        for (size_t j = 0; j < cols; j++)
            (*matrix)[i][j] = input[j];
    }

    /* display our input */
    for (size_t i = 0; i < word_count; i++)
        printf("WORD: %s\n", words[i]);

    print_matrix(rows, cols, matrix);

    /* free the memory */
    for (size_t i = 0; i < word_count; i++)
        free(words[i]);
    free(words);
    free(matrix);
}

int main(int argc, char **argv) {
    if (argc != 2)
        panic("usage: %s FILENAME", argv[0]);

    FILE *file = fopen(argv[1], "r");

    if (!file)
        panic("Could not open file [%s].", argv[1]);

    run(file);
    fclose(file);
}

stdout

WORD: nail
WORD: taco
WORD: name
WORD: men
WORD: next
WORD: can
+------+
| next |
|namect|
|  e aa|
|  n nc|
| nail |
+------+

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM