0

我认为我找到了一个很好的解决方案,可以不读取对于数组来说太大的值。

我正在使用这段代码(我知道它并不完美;它主要只是说明我的问题):

char myString[25][10]

while(fscanf(fp, "%24s", myString[i]) != EOF && i < MAX){
    i++;
}

获取输入并将其读入数组。如果该行超过 25 个字符,则将其拆分并将较长的部分放在 2d 数组(行)的下一部分。哪个工作正常,直到你接近尾声,所以你在第 8 行,你得到一条 75 长的线,它想把它分成数组中的 3 行。但是你在数组中只有两行空间,所以它崩溃了。

我不知道如何以动态的方式走这条线。我不必在 while 循环中执行此操作,但如何获取下一行并测量其长度。您如何动态执行这些读取操作和 malloc?

4

2 回答 2

1

使用固定大小的动态字符串数组fgets()

使用fgets()strdup()是这里唯一不完全可移植的部分:

char *data[10];
int   i;                             // Needs to last longer than the loop

for (i = 0; i < 10; i++)
{
    char buffer[4096];
    if (fgets(buffer, sizeof(buffer), fp) == 0)
        break;
    buffer[strlen(buffer)-1] = '\0'; // Zap last character; normally newline
    data[i] = strdup(buffer);        // Error check allocation?
}

如果疯子使用它来读取包含超过 4095 个字符的单行数据的大型 JSON 文件,这可能会遇到问题。对于大多数目的,您不太可能遇到 4 KiB 的行,但这是您的判断。

使用固定大小的动态字符串数组getline()

使用 POSIX 2008 getline()— 不同的可移植性问题:

char *data[10];
int   i;

for (i = 0; i < 10; i++)
{
    char   *buffer = 0;
    size_t  buflen = 0;
    ssize_t actlen;
    if ((actlen = getline(&buffer, &buflen, fp)) < 0)
        break;
    buffer[actlen-1] = '\0';       // Zap last character; normally newline
    data[i] = buffer;
}

这不会对行的长度施加任何上限,并getline()分配所有空间。

请注意,我没有检查 zapped 字符是否是任一片段中的换行符。您可以(可以说应该)添加该检查。您可能有一个文件末尾没有换行符;它在技术上不是一个文本文件(它们总是以换行符结尾),但它在 Unix 系统上有效。

固定大小字符串的固定大小数组

保留预先分配的内存:

char data[10][26];
int  i;

for (i = 0; i < 10; i++)
{
    if (fscanf(fp, "%25s", data[i]) != 1)
        break;
    int c;  // Gobble new line
    while ((c = getc(fp)) != EOF && c != '\n')
        ;
}

请注意,这读取的是单词,而不是行。它停在空白处。要读取行,您将使用扫描集转换规范:

   if (fscanf(fp, "%25[^\n]", data[i] != 1)

然后,您必须决定是否像以前一样吞噬该行的其余部分,或者是否插入一个微妙但至关重要的空格" %25[^\n]"来吞噬空白(在开始转换之前吃掉空白)。

动态字符串的动态数组

char   **data   = 0;
size_t   numstr = 0;  /* Number of strings in use */
size_t   maxstr = 0;  /* Number of pointers allocated */

char   *buffer = 0;
size_t  buflen = 0;
ssize_t actlen;

while ((actlen = getline(&buffer, &buflen, fp)) > 0)
{
    if (numstr >= maxstr)
    {
        assert(numstr == maxstr);
        size_t newnum = maxstr * 2 + 2;
        void  *newspc = realloc(data, newnum * sizeof(char *));
        if (newspc == 0)
        {
            /* memory allocation failed - data still valid */
            break;
        }
        maxstr = newnum;
        data = newspc;
    }
    buffer[actlen-1] = '\0';       // Zap last character; normally newline
    data[numstr++] = buffer;
    buffer = 0;                    // Reset so getline() allocates on next read
    buflen = 0;
}

不是每个人都赞成使用realloc()最初分配然后重新分配内存空间;malloc()如果你愿意,你可以在循环之前做一个。确保您在第2 * maxstr + 2一次分配时获得非零计数(实际上是 2),并且足够小以测试重新分配代码(一个好主意)。每次翻倍摊销分配的成本。在循环之后,您可以将data数组重新分配以缩小到实际大小:

realloc(data, numstr * sizeof(char *));

你应该检查它没有失败,但它不应该这样做。这样做是否真的值得商榷。


警告

上面代码的一个变体现在已经用编译器和测试程序进行了正式测试。 SSCCE 如下所示。

请注意,分配的内存不会在上方(或下方)释放。始终确保您知道何时释放分配的内存。通常,这意味着您需要一个合适的功能来完成这项工作;在程序退出时让 O/S 释放内存通常是不可接受的。

SSCCE(简短、独立、正确的示例

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

static void dump_strings(FILE *fp, const char *tag, size_t num, char **data)
{
    fprintf(fp, "%s:\n", tag);
    for (size_t i = 0; i < num; i++)
        fprintf(fp, " %2zu: [%s]\n", i, data[i]);
}

static void func1(FILE *fp)
{
    char *data[10];
    int   i;                             // Needs to last longer than the loop

    for (i = 0; i < 10; i++)
    {
        char buffer[4096];
        if (fgets(buffer, sizeof(buffer), fp) == 0)
            break;
        buffer[strlen(buffer)-1] = '\0'; // Zap last character; normally newline
        data[i] = strdup(buffer);        // Error check allocation?
    }
    dump_strings(stdout, "func1", i, data);
    /* Leak! */
}

static void func2(FILE *fp)
{
    char *data[10];
    int   i;

    for (i = 0; i < 10; i++)
    {
        char   *buffer = 0;
        size_t  buflen = 0;
        ssize_t actlen;
        if ((actlen = getline(&buffer, &buflen, fp)) < 0)
            break;
        buffer[actlen-1] = '\0';       // Zap last character; normally newline
        data[i] = buffer;
    }
    dump_strings(stdout, "func2", i, data);
    /* Leak! */
}

static void func3(FILE *fp)
{
    char   data[10][26];
    size_t i;

    for (i = 0; i < 10; i++)
    {
        if (fscanf(fp, "%25[^\n]", data[i]) != 1)
            break;
        int c;
        while ((c = getc(fp)) != EOF && c != '\n')
            ;
    }
    printf("%s:\n", "func3");
    for (size_t j = 0; j < i; j++)
        printf("%2zu: [%s]\n", j, data[j]);
}

static void func4(FILE *fp)
{
    char   **data   = 0;
    size_t   numstr = 0;  /* Number of strings in use */
    size_t   maxstr = 0;  /* Number of pointers allocated */

    char    *buffer = 0;
    size_t   buflen = 0;
    ssize_t  actlen;

    while ((actlen = getline(&buffer, &buflen, fp)) > 0)
    {
        if (numstr >= maxstr)
        {
            assert(numstr == maxstr);
            size_t newnum = maxstr * 2 + 2;
            void  *newspc = realloc(data, newnum * sizeof(char *));
            if (newspc == 0)
            {
                /* memory allocation failed - data still valid */
                break;
            }
            maxstr = newnum;
            data = newspc;
        }
        buffer[actlen-1] = '\0';       // Zap last character; normally newline
        data[numstr++] = buffer;
        buffer = 0;                    // Reset so getline() allocates on next read
        buflen = 0;
    }
    dump_strings(stdout, "func4", numstr, data);
    /* Leak! */
}

int main(void)
{
    func1(stdin);
    func2(stdin);
    func3(stdin);
    func4(stdin);
    return(0);
}

除了像筛子一样漏水外,valgrind说这还可以。它主要是在自己的源代码上进行测试的。

于 2013-03-25T21:21:20.237 回答
0
// assume the file is open in binary
fseek(fp, 0, SEEK_END);
size_t fpSize = ftell(fp);
char * myBuffer = new char[fpSize];
fseek(fp, 0, SEEK_SET);
fread(myBuffer, 1, fpsize, fp);
char * line = strtok(myBuffer, "\n");
while(line != 0)
{
   process(line);
}
delete [] myBuffer;

如果您实际上是在使用 C 编程,请先分配变量并使用 malloc 而不是 new,使用 free 而不是 delete[]。

如果您实际上是在使用 C++ 编程,请考虑使用 C++ I/O 并读入 std::string。

于 2013-03-25T21:32:26.647 回答