如何读取char数组(名称数组)?

时间:2017-06-17 04:57:07

标签: c arrays pointers char

伙计我用它来从文件中成功读取数据。

void read(FILE *fPtr, int *a, int *b, char **c, int size)
{
    char line[100];
    int num = 0;

    while ( fgets(line, 99, fPtr) && num < size )
    {       
        // This is a names array
        c[num] = strtok (line, " ");        
        // here the name prints out fine
        a[num] = atoi(strtok (NULL, " \n"));
        b[num] = atoi(strtok (NULL, " \n"));

        num++;
    }

}

但是我无法从这个char **数组中正确读取。

主要功能:

int main()
{
    FILE *fp;
    int g[30];
    int a[30];
    char *names[30];    

    // Open file 
    fp = fopen("input.txt", "r");

    read(
        fp, g, a, 
        names, 30 );

    printf("%s\n", player_names[0]);

    printThis(
        g, a,
        names, 30 );

    return 0;
}

打印这个:

void printThis(int* g, int* a, char** n, int s)
{
    for (int i = 0; i < 5; ++i)
        printf("%s\n", n[i]);
}

这完全没有打印名字!它打印只是@一些空间字符和@。为什么不打印任何东西。这不是访问char数组的正确方法吗?

enter image description here

2 个答案:

答案 0 :(得分:2)

正如评论中所述,您希望分配c[num] = strtok (line, " ");,您发现names中的值不再指向任何有意义的内容。在函数中声明变量时,除非为变量动态分配存储(例如,使用malloc),否则该变量仅在函数的生命周期内存活。为什么?

调用函数时,会为执行名为函数堆栈框架的函数创建单独的内存区域。该内存保存在函数内声明的变量。当函数返回时,堆栈帧被销毁(实际上只是根据需要释放以便重用)。声明为函数本地的任何变量都不再可用于访问。

在您的情况下,strtok会返回指向line内令牌开头的指针(如果找不到令牌,则返回NULL)。在函数return上,strtok返回的指针指向已释放以供重用的内存,并且无法再在调用函数(main这里)中访问 - 因此您的问题。

我该如何解决这个问题?嗯,有很多方法。第一个最简单的解决方案是为名称动态分配存储空间,并复制strtok找到的标记,并将它们存储在已分配的内存中。该内存将存活return,并且可以在调用者中有效访问。 (你必须记住在不再需要时释放内存)

您还需要让read(我的readfp下方)函数返回有意义的值,而不是void。知道实际填充了多少记录会很高兴。如果您将函数类型更改为int,则可以返回num并知道填充了多少记录(例如name[x] a[x] & g[x]组合)。

等待...... num被宣布为read的本地,我为什么要退回呢?每个函数总是可以返回自己的type值。如果您声明int read (...)read可以返回int值,并且由于调用方和被调用方之间的调用约定,它的生存返回。

将这些内容组合在一起,并使用strtol代替atoi进行int转换(并通过创建单独的函数来处理并轻松实现验证转换),你可以做类似以下的事情。另请注意,main使用参数,您可以使用它来传递文件名以打开,而不是硬编码,例如fopen("input.txt", "r")

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>

/* define constants for use below */ 
enum { BASE = 10, MAXP = 30, MAXL = 100 };

int readfp (FILE *fp, int *a, int *b, char **c, int size);
int xstrtol (char *s, int base);

int main (int argc, char **argv) {

    int g[MAXP] = {0},
        a[MAXP] = {0},
        n = 0;
    char *names[MAXP] = {NULL};    
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    n = readfp (fp, g, a, names, MAXP );

    if (fp != stdin) fclose (fp);     /* close file if not stdin */

    if (n)
        for (int i = 0; i < n; i++) {
            printf ("%-30s %8d %8d\n", names[i], g[i], a[i]);
            free (names[i]);    /* free memory allocated by strdup */
        }

    return 0;
}

int readfp (FILE *fp, int *a, int *b, char **c, int size)
{
    char line[MAXL] = "";
    int num = 0;

    while (num < size && fgets (line, sizeof line, fp))
    {       
        char *delim = " \t\n",
            *tmp = strtok (line, delim);
        int itmp = 0;

        if (tmp)
            c[num] = strdup (tmp);  /* allocate storage and copy tmp */
        else {
            fprintf (stderr, "error: strtok failed - c[%d].\n", num);
            break;
        }

        if ((tmp = strtok (NULL, delim)) && (itmp = xstrtol (tmp, BASE)))
            a[num] = itmp;
        else {
            fprintf (stderr, "error: strtok failed - a[%d].\n", num);
            break;
        }

        if ((tmp = strtok (NULL, delim)) && (itmp = xstrtol (tmp, BASE)))
            b[num] = itmp;
        else {
            fprintf (stderr, "error: strtok failed - b[%d].\n", num);
            break;
        }

        num++;
    }

    return num;
}

int xstrtol (char *s, int base) 
{
    errno = 0;
    char *endptr = NULL;
    long v = strtol (s, &endptr, base);

    if (errno) {
        fprintf (stderr, "xstrtol() error: over/underflow detected.\n");
        exit (EXIT_FAILURE);
    }

    if (s == endptr && v == 0) {
        fprintf (stderr, "xstrtol() error: no digits found.\n");
        exit (EXIT_FAILURE);
    }

    if (v < INT_MIN || INT_MAX < v) {
        fprintf (stderr, "xstrtol() error: out of range of integer.\n");
        exit (EXIT_FAILURE);
    }

    return (int)v;
}

示例输入文件

$ cat dat/names.txt
Ryan,Elizabeth     62  325
McIntyre,Osborne   84  326
DuMond,Kristin     18  327
Larson,Lois        42  328
Thorpe,Trinity     15  329
Ruiz,Pedro         35  330
Ali,Mohammed       60  331
Vashti,Indura      20  332

示例使用/输出

$ ./bin/readnames <dat/names.txt
Ryan,Elizabeth                       62      325
McIntyre,Osborne                     84      326
DuMond,Kristin                       18      327
Larson,Lois                          42      328
Thorpe,Trinity                       15      329
Ruiz,Pedro                           35      330
Ali,Mohammed                         60      331
Vashti,Indura                        20      332

内存使用/错误检查

在你编写的动态分配内存的任何代码中,你有2个职责关于任何分配的内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放

您必须使用内存错误检查程序,以确保您不会尝试在已分配的内存块的范围之外/之外进行写入,尝试读取或基于未初始化值的条件跳转,最后,确认您释放了已分配的所有内存。

对于Linux valgrind是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。

$ valgrind ./bin/readnames <dat/names.txt
==11303== Memcheck, a memory error detector
==11303== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==11303== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==11303== Command: ./bin/readnames
==11303==
Ryan,Elizabeth                       62      325
McIntyre,Osborne                     84      326
DuMond,Kristin                       18      327
Larson,Lois                          42      328
Thorpe,Trinity                       15      329
Ruiz,Pedro                           35      330
Ali,Mohammed                         60      331
Vashti,Indura                        20      332
==11303==
==11303== HEAP SUMMARY:
==11303==     in use at exit: 0 bytes in 0 blocks
==11303==   total heap usage: 8 allocs, 8 frees, 112 bytes allocated
==11303==
==11303== All heap blocks were freed -- no leaks are possible
==11303==
==11303== For counts of detected and suppressed errors, rerun with: -v
==11303== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存并且没有内存错误。

仔细看看,如果您有其他问题,请告诉我。

答案 1 :(得分:1)

您的代码中存在许多问题,正如评论中的人员所提到的那样。但是你应该仔细考虑Weather Vane的评论。现在你的名字数组是char *names[30];并为其分配局部变量的地址。您需要以某种方式复制数据(因为函数调用后行超出范围)。所以我建议将names数组更改为:

char names[30][WORD_LEN];

要复制数据,您可能需要strcpy之类的内容。我稍微改变了你的代码,并且能够看到所需的结果:

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

#define WORD_LEN 100

void read(FILE *fPtr, int *a, int *b, char c[][WORD_LEN], int size)
{
    char line[100];
    int num = 0;

    while ( fgets(line, 99, fPtr) && num < size ) {       
        // This is a names array
        //strcpy(c[num], strtok (line, " "));

        // here the name prints out fine
        // !avoid atoi, use sscanf instead
        //a[num] = atoi(strtok (NULL, " "));
        //b[num] = atoi(strtok (NULL, " "));

        sscanf(line, "%s%d%d", c[num], &a[num], &b[num]);
        num++;
    }
}

void printThis(int* g, int* a, char n[][WORD_LEN], int s)
{
    for (int i = 0; i < 2; ++i)
        printf("%s %d %d\n", n[i], a[i], g[i]);
}

int main()
{
    FILE *fp;
    int g[30];
    int a[30];
    char names[30][WORD_LEN];    

    // Open file 
    fp = fopen("input.txt", "r");
    read(fp, g, a, names, 30 );
    printThis(g, a, names, 30 );
    return 0;
}

我将文件结构更改为以空格分隔的标记,我能够看到名称:

~/work : $ cat input.txt 
Rohan 120000 4300000
Prashant 12 43
~/work : $ g++ readCharArray.c 
~/work : $ ./a.out 
Rohan 4300000 120000
Prashant 43 12
~/work : $