【问题标题】:File IO for unknown number of strings in CC中未知数量字符串的文件IO
【发布时间】:2016-01-29 02:27:19
【问题描述】:

我对 C++ 有一点经验,对于文件 IO,我会使用 getline 和向量来追加新行。我现在需要弄清楚如何在 C 中将 32 位数字列表读取为字符串,但我不知道提前有多少行。这是我习惯的做法:

vector<string> getFileData()
{// Open file, read in, close, return string vector to main
    ifstream myfile;
    string line;
    vector<string> iflines;

    myfile.open("samplefile.txt");
    while (getline(myfile, line))
        iflines.push_back(line);
    myfile.close();
    return iflines;
}

我读到我需要使用 malloc 和 realloc,但我对计算机体系结构知之甚少,所以如果有人能向我解释其中的一般原则或概念,我将不胜感激。我很难理解这些将如何给我一个包含字符串列表的变量,我可以返回到 main 而无需预先分配任何东西。另外,我应该使用什么类型的变量来存储 C 中的字符串列表、二维字符数组或其他东西?

【问题讨论】:

  • 您已标记此 c,但您的代码是 C++。如果您正在编写 C,您实际上必须自己重新分配内存。如果您实际使用的是 C++,那么您的代码就可以了——vector 会自动处理重新分配。
  • 两种可能:一种是预先检查文件,找到每行的行数和长度,然后根据情况进行分配。另一种(通常更合理)类似于vector 所做的:分配一些空间,当/如果你用完时,分配更多并将数据从旧分配复制到新分配。
  • 通常以char **lines; 开头,这将是一个动态分配的字符串数组。从那里你有两个选择。您可以分配一个大块来保存整个文件,并通过用\0 覆盖每个\n 将其分解为字符串,并将行首的地址放入lines 的下一个位置。其次,您可以一次将一行读入(大)固定缓冲区,为该行分配所需的空间,复制数据,然后将指针存储到lines 上的下一个点。
  • @austin 阅读realloc man page。这是改变动态分配缓冲区大小的典型方法。
  • 请注意,POSIX 有一个getline() 处理输入,这与 C++ 的getline() 类似(但接口当然有些不同)。您只需要 (a) 确保分配一个指针数组,并根据需要对其进行增长(但不能一次增加一个指针;每次分配更多空间时将大小加倍),以及 (b) 确保getline() 每次使用时都会分配一个新缓冲区。或者,您可以使用strdup() 复制字符串,让getline() 重复使用它分配的空间。

标签: c string file-io malloc


【解决方案1】:

处理您在 C 中尝试执行的操作的标准方法是,将未知数量的行(长度未知)读取到 quote 行的“数组”中,以分配一些合理预期的指针数量(使用malloccalloc),为每一行读取/分配存储,将行分配给一个空指针并重复直到达到指针的限制。当您达到原始指针限制时,只需 realloc 将您必须的指针数量增加到当前的 2 倍并继续前进。

然后您可以根据需要解析该行。在您的情况下,将每一行转换为 32 位值。要读取每一行,C 中line-oriented 输入的标准工具是fgetsgetline。由于您从 C++ 中提到了getline,因此不相关的 C 函数使用如下所示。 getline 将分配足够的空间来容纳读取的每一行数据。但是,它重用了自己的缓冲区,因此您需要为每一行分配存储空间并复制getline 返回的行。 (strdup 可以在一次通话中完成这两项工作)。

查看以下内容,如果您有任何问题,请告诉我。 注意:enum 仅用于定义分配的初始指针数量的常量。你可以对#define MAXL 64 做同样的事情。另请注意,行索引“idx”作为指向readfile 函数的指针传递,因此当readfile 返回时,读取的行数可以在main 中返回:

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

enum { MAXL = 64 };

char **readfile (FILE *fp, size_t *idx);
void *xcalloc (size_t n, size_t s);
void *xrealloc_dp (void *ptr, size_t *n);
FILE *xfopen (const char *fn, const char *mode);

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

    char **iflines = NULL;
    size_t i, idx = 0;
    FILE *fp = argc > 1 ? xfopen (argv[1], "r") : stdin;

    /* read file into dynamically allocated 'iflines' */
    if (!(iflines = readfile (fp, &idx))) return 1;

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

    for (i = 0; i < idx; i++)       /* print lines */
        printf (" line[%2zu] : %s\n", i, iflines[i]);

    for (i = 0; i < idx; i++)   /* free allocated memory */
        free (iflines[i]);
    free (iflines);

    return 0;
}

/* return allocated pointer to array of pointers containing
 * lines from 'fp'
 */
char **readfile (FILE *fp, size_t *idx)
{
    if (!fp) return NULL;

    char **filebuf = NULL;
    char *line = NULL;
    size_t maxl = MAXL, n = 0;
    ssize_t nchr = 0;

    /* allocate MAXL pointers */
    filebuf = xcalloc (MAXL, sizeof *filebuf);

    while ((nchr = getline (&line, &n, fp)) != -1)
    {   /* trim trailing newline or carriage return */
        while (nchr && (line[nchr-1] == '\n' || line[nchr-1] == '\r'))
            line[--nchr] = 0;

        filebuf[(*idx)++] = strdup (line); /* allocate & copy */

        /* realloc as required */
        if (*idx == maxl) filebuf = xrealloc_dp (filebuf, &maxl);
    }
    free (line);  /* free getline allocated memory */

    return filebuf;
}

void *xcalloc (size_t n, size_t s)
{
    register void *memptr = calloc (n, s);
    if (memptr == 0) {
        fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n");
        exit (EXIT_FAILURE);
    }

    return memptr;
}

void *xrealloc_dp (void *ptr, size_t *n)
{
    void **p = ptr;
    void *tmp = realloc (p, 2 * *n * sizeof tmp);
    if (!tmp) {
        fprintf (stderr, "xrealloc_dp() error: virtual memory exhausted.\n");
        exit (EXIT_FAILURE);
    }
    p = tmp;
    memset (p + *n, 0, *n * sizeof tmp); /* set new pointers NULL */
    *n *= 2;

    return p;
}

FILE *xfopen (const char *fn, const char *mode)
{
    FILE *fp = fopen (fn, mode);

    if (!fp) {
        fprintf (stderr, "xfopen() error: file open failed '%s'.\n", fn);
        // return NULL;      /* return or exit as desired */
        exit (EXIT_FAILURE);
    }

    return fp;
}

示例输入

$ cat dat/10int_nl.txt
8572
-2213
6434
16330
3034
12346
4855
16985
11250
1495

输出

$ ./bin/getline_min_fn dat/10int_nl.txt
 line[ 0] : 8572
 line[ 1] : -2213
 line[ 2] : 6434
 line[ 3] : 16330
 line[ 4] : 3034
 line[ 5] : 12346
 line[ 6] : 4855
 line[ 7] : 16985
 line[ 8] : 11250
 line[ 9] : 1495

注意: xcallocxrealloc_dpxfopen 只是辅助函数,它们对各自的函数 callocreallocfopen 进行适当的错误检查。 xrealloc_dp 名称只是表明它正在重新分配一个 pointer-to-pointer-to-type(通常称为 double-pointer)。因此,xrealloc_dp 名称。

仔细阅读,如果您有任何问题,或者我误解了您的问题,请告诉我。

【讨论】:

  • 没问题,很高兴能帮上忙。一旦你看到它完成了,就不难理解了,但是第一次从头开始组装起来是一个挑战。这种方法有许多变体,但它们大部分都遵循相同的方案。祝你编码顺利。
  • 是的,通读一遍很有意义,但我可能需要几天时间才能自己拼凑起来。非常感谢。
  • 当然,还要注意,这种方法会将任何文本文件读入iflines。编译后,让它读取它自己的源文件。您提到了数字,如果您需要将行转换为 32 位值,那么您仍然需要执行转换(例如 int value = atoi (iflines[x]); 或使用 float value = strtof (iflines[x], &amp;ep);)或使用任何其他 strtoX 系列转换。如果您遇到任何其他问题,请告诉我。
  • 谢谢,它工作得很好。我想我能够以字符串的形式进行我需要的操作,但我一定会牢记这些额外的建议。
猜你喜欢
  • 2017-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多