如何使用C来逐行读取txt文件(int,未知长度)?

时间:2018-04-09 08:19:39

标签: c arrays scanf sizeof fgets

我想合并文本文件中的数据排序。(逐行)

为了使我的合并排序起作用,我必须逐行读取数据,并将它们放入数组中以便对它们进行排序。 (我们只知道每行最多10000个整数) 所以我做了我的研究并尝试了这些方法:

1。使用fgets和strtok / strtol。

问题:我不知道char数组的最大长度。此外,声明一个巨大的数组可能会导致缓冲区溢出。

来源:How many chars can be in a char array?

2。使用fscanf输入整数数组。

问题:相同。我不知道有多少整数排成一行。所以我不会对“%d”部分感到满意​​。(不知道应该有多少)

3。使用fscanf以char数组的形式输入,并使用strtok/strtol

问题:相同。由于我不知道长度,我不能做像

这样的事情
char *data;
data = malloc(sizeof(char) * datacount);

因为“数据帐户”未知。

有什么出路吗?

更新

示例输入:

-16342 2084 -10049 10117 2786
3335 3512 -10936 5343 -1612 -4845 -14514

示例输出:

-16342 -10049 2084 2786 10117
-14514 -10936 -4845 -1612 3335 3512 5343

3 个答案:

答案 0 :(得分:1)

你说:

  

我们只知道每行最多有10000个整数

所以就去吧。符合标准的编译器(和环境)应该能够定义最多65,535个对象的数组。 10,000远不如此,只需定义一个静态数组:

int a[10001], n;

int main() {
    // Open file
    n = 0;
    while (fscanf(fp, " %d", &a[n]) == 1) {
        // Process a[n]
        n++;
    }
}

如果您了解平台规范(如sizeof(int) == 4),则可以假设整数长度。例如,32位整数的最大长度为11个字符(在-2147483648处)左右。然后,您可以定义具有计算长度的char数组。

答案 1 :(得分:0)

您确实可以使用fscanf来读取各个整数。除此之外,您还需要了解指针和malloc,还要了解realloc

您只需执行类似

的操作
int temporary_int;
int *array = NULL;
size_t array_size = 0;

while (fscanf(your_file, "%d", &temporary_int) == 1)
{
    int *temporary_array = realloc(array, (array_size + 1) * sizeof(int));
    if (temporary_array != NULL)
    {
        array = temporary_array;
        array[array_size++] = temporary_int;
    }
}

在该循环之后,如果array不是空指针,那么它将包含文件中的所有整数,无论​​有多少整数。大小(元素数量)位于array_size变量中。

看到更新后,您可以更轻松地了解所需内容。

在伪代码中很容易:

while(getline(line))
{
    array_of_ints = create_array_of_ints();

    for_each(token in line)
    {
        number = convert_to_integer(token);
        add_number_to_array(array_of_ints, number);
    }

    sort_array(array_of_ints);
    display_array(array_of_ints);
}

实际上实现这一点要困难得多,并且在某种程度上取决于您的环境(例如,如果您有权访问the POSIX getline function)。

如果你有e..g getline(或类似函数),那么伪代码中的外部循环很容易,并且会看起来它已经做了什么。否则,您基本上必须逐个字符地读取到动态扩展的缓冲区(使用realloc)以适合整行。

这将我们带到外部循环的内容:将输入拆分为一组值。这个答案中第一个代码片段已经有的基本解决方案,我在循环中根据需要重新分配数组。要分割值,那么strtok可能是最简单的一个。如果您不关心验证输入,则可以使用strtol atoi(如果您需要验证)转换为整数。

请注意,您并非真正需要动态分配数组。在int仅为"且仅为#34;的当前系统上,将有10000 sizeof(int) == 4个值40000字节。这个小到足以适应大多数非嵌入式系统的堆栈。

答案 2 :(得分:0)

您没有考虑的一种方法可能是最便携的方法:编写您自己的“从FILE流中读取一个令牌,并将其转换为long;但永远不要越过换行边界”功能:

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

/* Reads one decimal integer (long) from 'input', saving it to 'to'.
   Returns: 0    if success,
            EOF  if end of input,
            '\n' if newline (end of line),
            '!'  if the number is too long, and
            '?'  if the input is not a number.
*/
int read_long(FILE *input, long *to)
{
    char    token[128], *end;
    size_t  n = 0;
    long    value;
    int     c;

    /* Consume leading whitespace, excluding newlines. */
    do {
        c = fgetc(input);
    } while (c == '\t' || c == '\v' || c == '\f' || c == ' ');

    /* End of input? */
    if (c == EOF)
        return EOF;

    /* Newline? */
    if (c == '\n' || c == '\r') {
        /* Do not consume the newline character! */
        ungetc(c, input);
        return '\n';
    }

    /* Accept a single '+' or '-'. */
    if (c == '+' || c == '-') {
        token[n++] = c;
        c = fgetc(input);
    }

    /* Accept a zero, followed by 'x' or 'X'. */
    if (c == '0') {
        token[n++] = c;
        c = fgetc(input);
        if (c == 'x' || c == 'X') {
            token[n++] = c;
            c = fgetc(input);
        }
    }

    /* Accept digits. */
    while (c >= '0' && c <= '9') {
        if (n < sizeof token - 1)
            token[n] = c;
        n++;
        c = fgetc(input);
    }

    /* Do not consume the separator. */
    if (c != EOF)
        ungetc(c, input);

    /* No token? */
    if (!n)
        return '?';

    /* Too long? */
    if (n >= sizeof token)
        return '!';

    /* Terminate token, making it a string. */
    token[n] = '\0';

    /* Parse token. */
    end = token;
    errno = 0;
    value = strtol(token, &end, 0);
    if (end != token + n || errno != 0)
        return '?';

    /* Save value. */
    if (to)
        *to = value;

    return 0;
}

要前进到下一行,您可以使用例如

/* Skips the rest of the current line,
   to the beginning of the next line.
   Returns: 0 if success (next line exists, although might be empty)
          EOF if end of input.
*/
int next_line(FILE *input)
{
    int  c;

    /* Skip the rest of the current line, if any. */
    do {
        c = fgetc(input);
    } while (c != EOF && c != '\n' && c != '\r');

    /* End of input? */
    if (c == EOF)
        return EOF;

    /* Universal newline support. */
    if (c == '\n') {
        c = fgetc(input);
        if (c == EOF)
            return EOF;
        else
        if (c == '\r') {
            c = fgetc(input);
            if (c == EOF)
                return EOF;
        }
    } else
    if (c == '\r') {
        c = fgetc(input);
        if (c == EOF)
            return EOF;
        else
        if (c == '\n') {
            c = fgetc(input);
            if (c == EOF)
                return EOF;
        }
    }

    ungetc(c, input);
    return 0;
}

要读取每一行的长片,您可以使用动态调整大小的缓冲区,跨行共享:

int main(void)
{
    long   *field_val = NULL;
    size_t  field_num = 0;
    size_t  field_max = 0;

    int     result;

    do {
        /* Process the fields in one line. */
        field_num = 0;

        do {

            /* Make sure the array has enough room. */
            if (field_num >= field_max) {
                void *temp;

                /* Growth policy; this one is linear (not optimal). */
                field_max = field_num + 5000;

                temp = realloc(field_val, field_max * sizeof field_val[0]);
                if (!temp) {
                    fprintf(stderr, "Out of memory.\n");
                    return EXIT_FAILURE;
                }

                field_val = temp;
            }

            result = read_long(stdin, field_val + field_num);
            if (result == 0)
                field_num++;

        } while (result == 0);

        if (result == '!' || result == '?') {
            fprintf(stderr, "Invalid input!\n");
            return EXIT_FAILURE;
        }

        /*
         * You now have 'field_num' longs in 'field_val' array.
        */

        /* Proceed to the next line. */
    } while (!next_line(stdin));

    free(field_val);
    field_val = NULL;
    field_max = 0;

    return EXIT_SUCCESS;
}

虽然逐个字符读取输入并不是最有效的方式(它往往比逐行读取略慢),但它的多功能性可以补偿它。

例如,上述代码适用于任何换行符约定(CRLF或\r\n,LFCR或\n\r,CR \r和LF \n)(但在Windows中)您需要为"b"指定fopen()标记,以阻止它自行创建换行符。

逐字段读取方法也很容易扩展到例如CSV格式,包括其特有的引用规则,甚至嵌入的换行符。