如何从文件逐行读取?

时间:2019-05-29 12:48:45

标签: c system-calls

我有一个像这样的文件:

<int>
<int>
<int>
<strig1>
....
<stringk>

其中int是数字,string是路径。

我想将每个int保存到一个不同的变量中,与string相同。

如何仅使用C进行系统调用逐行读取?

如何使用read

1 个答案:

答案 0 :(得分:0)

您可以使用 fgets 或更简单的 getline 来读取行,以允许任意长度的行,而不必管理仅一部分行的读取,因为您的缓冲区太小,请使用 strtol 查看该行是否仅包含 int (在分隔符之外)

例如:

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

int main(int argc, char ** argv)
{
  if (argc != 2)
      printf("Usage : %s <file path>\n", *argv);
  else {
    FILE * fp = fopen(argv[1], "r");

    if (fp == NULL)
      fprintf(stderr, "cannot open '%s'\n", argv[1]);
    else {
      long * longs = malloc(0);
      char ** strings = malloc(0);
      size_t nlong = 0;
      size_t nstring = 0;
      char *lineptr = NULL;
      size_t n = 0;
      ssize_t len;

      while ((len = getline(&lineptr, &n, fp)) != -1) {
        errno = 0;

        char * endptr;
        long v = strtol(lineptr, &endptr, 10);

        if ((errno == 0) &&
            (*lineptr != 0) && (*lineptr != '\n') &&
            ((*endptr == 0) || (*endptr == '\n'))) {
          /* a valid long */
          longs = realloc(longs, (nlong + 1) * sizeof(long));
          longs[nlong++] = v;
        }
        else {
          /* consider a string, use strdup to limit needed size, lineptr can be long for nothing */
          if (lineptr[len - 1] == '\n')
            /* probably you do not want to save the newline */
            lineptr[--len] = 0;

          strings = realloc(strings, (nstring + 1) * sizeof(char *));
          strings[nstring] = malloc(len + 1);
          strcpy(strings[nstring++], lineptr);
        }
      }

      free(lineptr);
      fclose(fp);

      /* debug */
      printf("read %zu longs\n", nlong);
      for (size_t i = 0; i != nlong; ++i)
        printf("\t%ld\n", longs[i]);
      printf("read %zu strings\n", nstring);
      for (size_t i = 0; i != nstring; ++i)
        printf("\t'%s'\n", strings[i]);

      /* free memory */
      free(longs);
      for (size_t i = 0; i != nstring; ++i)
        free(strings[i]);
      free(strings);
    }
  }

  return 0;
}

编译和执行:

bruno@bruno-XPS-8300:/tmp$ gcc -pedantic -Wall -Wextra r.c
bruno@bruno-XPS-8300:/tmp$ cat f
123
qsd wxc
123 456
11111111111111111111111111111111111111111111111111
bruno@bruno-XPS-8300:/tmp$ ./a.out f
read 1 longs
    123
read 3 strings
    'qsd wxc'
    '123 456'
    '11111111111111111111111111111111111111111111111111'
bruno@bruno-XPS-8300:/tmp$ 

注意 11111111111111111111111111111111111111111111111111111 对于 long 而言太长,因此它被存储为字符串

valgrind 下执行:

bruno@bruno-XPS-8300:/tmp$ valgrind ./a.out f
==10003== Memcheck, a memory error detector
==10003== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10003== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==10003== Command: ./a.out f
==10003== 
read 1 longs
    123
read 3 strings
    'qsd wxc'
    '123 456'
    '11111111111111111111111111111111111111111111111111'
==10003== 
==10003== HEAP SUMMARY:
==10003==     in use at exit: 0 bytes in 0 blocks
==10003==   total heap usage: 13 allocs, 13 frees, 5,915 bytes allocated
==10003== 
==10003== All heap blocks were freed -- no leaks are possible
==10003== 
==10003== For counts of detected and suppressed errors, rerun with: -v
==10003== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
bruno@bruno-XPS-8300:/tmp$ 

如果您不想保存 long 而是保存 int ,则当 strtol 指示有效的 long 时,请对其进行比较强制转换为 int 以检测是否有 int 溢出的值,因此请替换

    if ((errno == 0) &&
        (*lineptr != 0) && (*lineptr != '\n') &&
        ((*endptr == 0) || (*endptr == '\n'))) {

作者

    if ((errno == 0) &&
        (*lineptr != 0) && (*lineptr != '\n') &&
        ((*endptr == 0) || (*endptr == '\n')) &&
        (v == ((int) v)) {

为了允许管理任意数量的数字/字符串,我使用了动态数组来保存它们