C getline()在EOF之前返回-1

时间:2015-11-17 04:58:37

标签: c eof getline

我在c程序中使用getline()函数从文件中读取行,将读取信息放入结构中,创建一个以该结构作为参数的线程,然后重复。 但是,getline()在EOF之前返回-1,在" id:test2"之后的第二组参数之后返回-1。在提供的文件中。
由于gdb中的print errno返回0,我认为没有错误,getline()似乎相信它已达到EOF。
创建线程部分已被注释掉,因为它与手头的问题无关。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/time.h>

#define BUFFER_SIZE 256

time_t cur;
char* keypath = "home/chy/.ssh/id_rsa";
int logfreq = 1;
int hashfreq = 180;

struct getArgs
{
  const char* id;
  const char* hostaddr;
  uint16_t port;
  const char* uname;
  const char* path;
};

int main(int argc, char **argv)
{
  int th_max = 5;
  int th_count = 0;
  pthread_t* ths = (pthread_t*) malloc(th_max * sizeof(pthread_t));

  FILE* fp;
  fp = fopen("config.txt", "r");

  char* line = NULL;
  char buf[128];
  int r = 0;
  size_t len = 0;
  ssize_t read;

  system("mkdir logs && chmod -R a+r logs");

  while(1)
  {
    pthread_t th;
//Get ID
    while((read = getline(&line, &l, fp)) != -1)
    {
      if(!(read > 0))
        continue;
      if(sscanf(line, "id: %[^\n]\n", buf) == 1)
        break;
      if(sscanf(line, "keypath: %[^\n]\n", buf) == 1)
        keypath = strdup(buf);
      else if(sscanf(line, "logfreq: %d\n", &r) == 1)
        logfreq = r;
      else if(sscanf(line, "hashfreq: %d\n", &r) == 1)
        hashfreq = r;
    }
    if(read == -1)
      break;
    struct getArgs* args = (struct getArgs*)malloc(sizeof(struct getArgs));
    args->id = strdup(buf);
//Get Host Address
    if(getline(&line, &len, fp) == -1)
    {
      free_args(args);
      break;
    }
    if(sscanf(line, "hostaddr: %[^\n]\n", buf) != 1)
    {
      free_args(args);
      continue;
    }
    args->hostaddr = strdup(buf);
//Get Port Number
    if(getline(&line, &len, fp) == -1)
    {
      free_args(args);
      break;
    }
    if(sscanf(line, "port: %d\n", &r) != 1)
    {
      free_args(args);
      continue;
    }
    args->port = r;
//Get Username
    if(getline(&line, &len, fp) == -1)
    {
      free_args(args);
      break;
    }
    if(sscanf(line, "username: %[^\n]\n", buf) != 1)
    {
      free_args(args);
      continue;
    }
args->uname = strdup(buf);
//def Path
    if(getline(&line, &len, fp) == -1)
    {
      free_args(args);
      break;
    }
    if(sscanf(line, "path: %[^\n]\n", buf) != 1)
    {
      free_args(args);
      continue;
    }
    args->path = strdup(buf);

//    int err = pthread_create(&th, NULL, &getFiles, args);

    if(th_count > th_max)
    {
      th_max *= 2;
      ths = (pthread_t*)realloc(ths, th_max * sizeof(pthread_t));
    }
    *(ths+th_count * sizeof(pthread_t)) = th;
    th_count++;
  }

  fclose(fp);

  while(1);
}

文件:

keypath: /home/username/.ssh/id_rsaNOPASSWORD
id: test1
hostaddr: XXX.XXX.XXX.XXX
port: 22
username: hpc
path: /home/hpc/
id: test2
hostaddr: XXX.XXX.XXX.XXX
port: 22
username: hpc
path: /home/hpc/
id: test3
hostaddr: XXX.XXX.XXX.XXX
port: 22
username: hpc
path: /home/hpc/
id: test4
hostaddr: XXX.XXX.XXX.XXX
port: 3844
username: uname
path: /home/uname/hpc/

最后一个getline()之前的文件指针的内容:

{_flags = -72539000,
  _IO_read_ptr = 0x7ffff7ff80d0 "    \nid: test3\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test4\nhostaddr: XXX.XXX.XXX.XXX\nport: 3844\nusername: uname\npath: /home/uname/hpc/\n\n",
  _IO_read_end = 0x401d10 "H\211l$\330L\211d$\340H\215-\277\006 ",
  _IO_read_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_write_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_write_ptr = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_write_end = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_buf_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., _IO_buf_end = 0x7ffff7ff9000 "P\220\377\367\377\177", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7bbb880, _fileno = 7, _flags2 = 0, _old_offset = 0, _cur_column = 0,
  _vtable_offset = 0 '\000', _shortbuf = "", _lock = 0x603120, _offset = -1, __pad1 = 0x0, __pad2 = 0x603130, __pad3 = 0x0, __pad4 = 0x0, __pad5 = 0, _mode = -1, _unused2 = '\000' <repeats 19 times>}

后:

{_flags = -72538984,
  _IO_read_ptr = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_read_end = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_read_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_write_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_write_ptr = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_write_end = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"...,
  _IO_buf_base = 0x7ffff7ff8000 "keypath: /home/username/.ssh/id_rsaNOPASSWORD\n    \nid: test1\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /home/hpc/\n    \nid: test2\nhostaddr: XXX.XXX.XXX.XXX\nport: 22\nusername: hpc\npath: /ho"..., _IO_buf_end = 0x7ffff7ff9000 "P\220\377\367\377\177", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7bbb880, _fileno = 7, _flags2 = 0, _old_offset = 0, _cur_column = 0,
  _vtable_offset = 0 '\000', _shortbuf = "", _lock = 0x603120, _offset = -1, __pad1 = 0x0, __pad2 = 0x603130, __pad3 = 0x0, __pad4 = 0x0, __pad5 = 0, _mode = -1, _unused2 = '\000' <repeats 19 times>}

我也试图用fgets()而不是getline()来实现它,但结果相同 可能导致这个问题的原因是什么?

编辑:
我已经在同一个文件上运行了代码,除了没有多余的空格/空行以及切换顺序以查看是否有任何影响,但结果是相同的。
我还可以确认test1和test2的结构已正确填充。

{id = 0x6032e0 "test1", hostaddr = 0x603300 "XXX.XXX.XXX.XXX", port = 22, uname = 0x603320 "hpc", path = 0x603340 "/home/hpc/"}
{id = 0x603390 "test2", hostaddr = 0x6033b0 "XXX.XXX.XXX.XXX", port = 22, uname = 0x6033d0 "hpc", path = 0x6033f0 "/home/hpc/"}

编辑: 版本更改为代码和文件

2 个答案:

答案 0 :(得分:3)

该行

sscanf(line, "id: %[^\n]\n", buf) != 1

不匹配空白行,导致您的读取循环在此时失去同步。进一步的问题从那里级联,因为id:test2被狼吞虎咽,最终你击中了EOF。

[      Matches a nonempty sequence of characters ...

答案 1 :(得分:2)

除了评论中讨论的 unsigned / signed type不匹配之外,您遇到的主要问题是测试中使用的冲突逻辑,以及多个松散组织的调用getlinesscanf您要测试if ((read = getline ... || sscanf ...) getlinesscanf(输入或匹配失败时)可能会返回EOF({{ 1}})无法告诉问题是什么。要清理逻辑,你想要读取配置文件,你在(1)读取行 - 一次,(2)解析标记和值,然后( 3)测试-1tag并采取必要的行动。

坚持这种方法,并忽略与您的读取问题无关的线程代码,我调试/重新编写了您的输入例程,为您提供了一种以合理的方式处理读取和分离的方法的示例。在这样做的过程中,我收集了一个指向struct的指针数组中的所有设置(例如value)。您可能不需要使用pthread方案收集结构数组中的每一个,但出于示例的目的,已进行了更改。

我还在下面添加了第二个示例,其中显示了一个额外的方法,可以进行更多检查,以确保在 id,hostaddr,port,username和path 组中读取您的值。第一个简单示例将处理在输入文件中跳过空行:

struct getArgs **args;

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define NARGS 32 #define TSIZE 16 #define VSIZE 256 char *keypath = "home/chy/.ssh/id_rsa"; int logfreq = 1; int hashfreq = 180; struct getArgs { const char *id; const char *hostaddr; uint16_t port; const char *uname; const char *path; }; void *xcalloc (size_t n, size_t s); void *xrealloc_dp (void *ptr, size_t *n); int main (int argc, char **argv) { struct getArgs **args = NULL; char *line = NULL; size_t len = 0; size_t idx = 0; size_t nargs = NARGS; size_t i; ssize_t read; FILE *fp = argc > 1 ? fopen (argv[1], "r") : fopen ("config.txt", "r"); if (!fp) { fprintf (stderr, "file open failed.\n"); return 1; } /* allocate NARGS pointer to struct getArgs */ args = xcalloc (NARGS, sizeof *args); /* read each line in file */ while ((read = getline (&line, &len, fp)) != -1) { if (read == 1) continue; /* skip blank lines */ char tag[TSIZE] = {0}; char val[VSIZE] = {0}; /* separate tag and value (val) */ if (sscanf (line, "%s %[^\n]%*c", tag, val) != 2) { fprintf (stderr, "error: sscanf conversion failed.\n"); break; } /* handle keypath, logfreq, hashfreq */ if (strcmp (tag, "keypath:") == 0) { keypath = strdup (val); continue; } if (strcmp (tag, "logfreq:") == 0) { logfreq = (int)strtol (val, NULL, 10); continue; } if (strcmp (tag, "hashfreq:") == 0) { hashfreq = (int)strtol (val, NULL, 10); continue; } /* allocate space for args[idx] if "id:", then handle id, hostaddr, port, username, path */ if (strcmp (tag, "id:") == 0) { args[idx] = calloc (1, sizeof **args); args[idx]->id = strdup (val); continue; } if (strcmp (tag, "hostaddr:") == 0) { args[idx]->hostaddr = strdup (val); continue; } if (strcmp (tag, "port:") == 0) { args[idx]->port = (uint16_t)strtoul (val, NULL, 10); continue; } if (strcmp (tag, "username:") == 0) { args[idx]->uname = strdup (val); continue; } /* increment idx on path */ if (strcmp (tag, "path:") == 0) args[idx++]->path = strdup (val); if (idx == nargs) /* check idx, realloc */ args = xrealloc_dp (args, &nargs); } fclose (fp); printf ("\n keypath : %s\n logfreq : %d\n hashfreq : %d\n", keypath, logfreq, hashfreq); for (i = 0; i < idx; i++) printf ("\n id : %s\n hostaddr : %s\n port : %hu\n" " username : %s\n path : %s\n", args[i]->id, args[i]->hostaddr, args[i]->port, args[i]->uname, args[i]->path); for (i = 0; i < idx; i++) free (args[i]); free (args); return 0; } /* calloc with error check, exit on failure */ 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; } /* reallocate memory for a double-pointer from 'n' to 2 * 'n' * returns pointer to reallocated block on success, exit on * failure */ 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; } xcalloc函数只是对xrealloc_dpcalloc(对于双指针)进行错误检查以保持主体和逻辑的函数。代码清楚。我在以下测试输入上运行代码并收到以下输出:

输入测试文件(带空行)

realloc

<强>输出

$ cat ../dat/idhostaddrport.txt
keypath: /home/username/.ssh/id_rsaNOPASSWORD
logfreq: 2
hashfreq: 250

id: test1
hostaddr: XXX.XXX.XXX.XXX
port: 221
username: hpc1
path: /home/hpc1/

id: test2
hostaddr: XXX.XXX.XXX.XXX
port: 222
username: hpc2
path: /home/hpc2/

id: test3
hostaddr: XXX.XXX.XXX.XXX
port: 223
username: hpc3
path: /home/hpc3/

id: test4
hostaddr: XXX.XXX.XXX.XXX
port: 3844
username: uname
path: /home/uname/hpc/

您应该努力的方法是进一步验证您的输入。确保如果您阅读$ ./bin/getline_sscanf_dbg ../dat/idhostaddrport.txt keypath : /home/username/.ssh/id_rsaNOPASSWORD logfreq : 2 hashfreq : 250 id : test1 hostaddr : XXX.XXX.XXX.XXX port : 221 username : hpc1 path : /home/hpc1/ id : test2 hostaddr : XXX.XXX.XXX.XXX port : 222 username : hpc2 path : /home/hpc2/ id : test3 hostaddr : XXX.XXX.XXX.XXX port : 223 username : hpc3 path : /home/hpc3/ id : test4 hostaddr : XXX.XXX.XXX.XXX port : 3844 username : uname ,则还会为每个id阅读剩余的值hostaddrpath。添加最少检查的一个简单变体是读取循环的以下更改:

id

看看两者,如果您有任何其他问题,请告诉我。