从C中的文本文件中读取值流

时间:2015-04-27 21:46:42

标签: c arrays file text malloc

我有一个文本文件,其中可能包含一个或最多400个数字。每个数字用逗号分隔,分号用于表示数字流的结尾。 目前我正在使用fgets逐行读取文本文件。出于这个原因,我使用1024个元素的固定数组(文本文件的每行最大字符数)。 这不是理想的实现方法,因为如果在文本文件中只输入一个数字,那么1024个元素的数组将毫无意义。 有没有办法使用带有malloc函数(或任何其他方法)的fgets来提高内存效率?

2 个答案:

答案 0 :(得分:0)

如果您打算在生产代码中使用它,那么我会请求您按照评论部分中的建议进行操作。

如果您的要求更多是学习或学习,那么这是一个复杂的方法。

伪码

1. Find the size of the file in bytes, you can use "stat" for this.
2. Since the file format is known, from the file size, calculate the number of items.
3. Use the number of items to malloc.

瞧! :P

如何查找文件大小

您可以使用stat,如下所示:

#include <sys/stat.h>
#include <stdio.h>

int main(void)
{
    struct stat st;

    if (stat("file", &st) == 0) {
        printf("fileSize: %d  No. of Items: %d\n", (st.st_size), (st.st_size/2));
        return st.st_size;
    }

    printf("failed!\n");
    return 0;
}

运行时此文件将返回文件大小:

$> cat file
1;
$> ./a.out
fileSize: 3  No. of Items: 1

$> cat file
1,2,3;
$> ./a.out
fileSize: 7  No. of Items: 3

免责声明:这种方法是否可以最大限度地减少预先分配的内存?在天堂没办法! :)

答案 1 :(得分:0)

为您的数据动态分配空间是在C中工作的基本工具。您还可以付出代价来学习。要记住的主要事情是,

  

“如果您分配内存,则您有责任跟踪其使用情况   并保留指向块的起始地址的指针   记忆,所以你可以在完成它后释放它。否则你的   带有泄漏记忆的代码,如筛子。“

动态分配是直截了当的。您分配一些初始内存块并跟踪您添加的内容。您必须测试每个分配是否成功。您必须测试您使用的内存块数量,并在完全时重新分配或停止写入数据,以防止写入超出内存块的末尾。如果您未能测试,则会破坏与代码关联的内存。

重新分配时,始终使用临时指针重新分配,因为重新分配失败时,将释放原始内存块。 (导致丢失该块中的所有先前数据)。使用临时指针允许您以某种方式处理故障,以便在需要时保留该块。

考虑到这一点,下面我们最初为64个long值分配空间(您可以轻松更改为代码以处理任何类型,例如intfloat,{{1} } ...)。然后代码读取每行数据(使用double为每行动态分配缓冲区)。 getline用于解析为strtol分配值的缓冲区。 array用作索引来跟踪已读取的值的数量,当idx达到当前idx时,nmax的重新分配次数是以前的两倍是和array已更新以反映更改。对文件中的每一行数据继续读取,解析,检查和重新分配。完成后,值将打印到stdout,显示从测试文件中读取的400个随机值,格式为nmax

为了保持读取循环逻辑的清洁,我将353,394,257,...293,58,135;转换的错误检查放入函数strtol中,但您可以将该代码包含在xstrtol中你喜欢。这同样适用于main()功能。要查看重新分配的时间,您可以使用realloc_long定义编译代码。 E.g:

-DDEBUG

程序希望您的数据文件名作为第一个参数,并且您可以提供可选的转换基础作为第二个参数(默认值为10)。 E.g:

gcc -Wall -Wextra -DDEBUG -o progname yoursourcefile.c

仔细查看,测试一下,如果您有任何问题,请告诉我。

./progname datafile.txt [base (default: 10)]

示例输出(使用-DDEBUG显示重新分配)

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

#define NMAX 64

long xstrtol (char *p, char **ep, int base);
long *realloc_long (long *lp, unsigned long *n);

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

    char *ln = NULL;                /* NULL forces getline to allocate  */
    size_t n = 0;                   /* max chars to read (0 - no limit) */
    ssize_t nchr = 0;               /* number of chars actually read    */
    size_t idx = 0;                 /* array index counter              */
    long *array = NULL;             /* pointer to long                  */
    unsigned long nmax = NMAX;      /* initial reallocation counter     */
    FILE *fp = NULL;                /* input file pointer               */
    int base = argc > 2 ? atoi (argv[2]) : 10; /* base (default: 10)    */

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

    /* allocate array of NMAX long using calloc to initialize to 0 */
    if (!(array = calloc (NMAX, sizeof *array))) {
        fprintf (stderr, "error: memory allocation failed.");
        return 1;
    }

    /* read each line from file - separate into array       */
    while ((nchr = getline (&ln, &n, fp)) != -1)
    {
        char *p = ln;      /* pointer to ln read by getline */ 
        char *ep = NULL;   /* endpointer for strtol         */

        while (errno == 0)
        {   /* parse/convert each number in line into array */
            array[idx++] = xstrtol (p, &ep, base);

            if (idx == nmax)        /* check NMAX / realloc */
                array = realloc_long (array, &nmax);

            /* skip delimiters/move pointer to next digit   */
            while (*ep && *ep != '-' && (*ep < '0' || *ep > '9')) ep++;
            if (*ep)
                p = ep;
            else
                break;
        }
    }

    if (ln) free (ln);              /* free memory allocated by getline */
    if (fp) fclose (fp);            /* close open file descriptor       */

    int i = 0;
    for (i = 0; i < idx; i++)
        printf (" array[%d] : %ld\n", i, array[i]);

    free (array);

    return 0;
}

/* reallocate long pointer memory */
long *realloc_long (long *lp, unsigned long *n)
{
    long *tmp = realloc (lp, 2 * *n * sizeof *lp);
#ifdef DEBUG
    printf ("\n  reallocating %lu to %lu\n", *n, *n * 2);
#endif
    if (!tmp) {
        fprintf (stderr, "%s() error: reallocation failed.\n", __func__);
        // return NULL;
        exit (EXIT_FAILURE);
    }
    lp = tmp;
    memset (lp + *n, 0, *n * sizeof *lp); /* memset new ptrs 0 */
    *n *= 2;

    return lp;
}

long xstrtol (char *p, char **ep, int base)
{
    errno = 0;

    long tmp = strtol (p, ep, base);

    /* Check for various possible errors */
    if ((errno == ERANGE && (tmp == LONG_MIN || tmp == LONG_MAX)) ||
        (errno != 0 && tmp == 0)) {
        perror ("strtol");
        exit (EXIT_FAILURE);
    }

    if (*ep == p) {
        fprintf (stderr, "No digits were found\n");
        exit (EXIT_FAILURE);
    }

    return tmp;
}

内存错误检查

$ ./bin/read_long_csv dat/randlong.txt

  reallocating 64 to 128

  reallocating 128 to 256

  reallocating 256 to 512
 array[0] : 353
 array[1] : 394
 array[2] : 257
 array[3] : 173
 array[4] : 389
 array[5] : 332
 array[6] : 338
 array[7] : 293
 array[8] : 58
 array[9] : 135
<snip>
 array[395] : 146
 array[396] : 324
 array[397] : 424
 array[398] : 365
 array[399] : 205