6

我正在编写一些 C 代码来处理文件中的一些数据,但我刚刚了解到该文件将被不断添加(大约 1 次/秒,也许更快)。所以我想知道如何在添加文件时继续读取文件。然后当我到最后,等到下一行被添加,然后处理它。然后再次等待,然后处理,依此类推。我有类似的东西:

while(1){
    fgets(line, sizeof(line), file);
    while(line == NULL){
       //wait ?  then try to read again?
    }
    //tokenize line and do my stuff here
}

我想我也许可以使用 inotify,但我对此无能为力。有人有建议吗?

4

4 回答 4

4

The most efficient way is using inotify, and the direct way is using the read() system call directly.

using inotify

The following code may give you some help, It works well on Debian 7.0, GCC 4.7:

/*This is the sample program to notify us for the file creation and file deletion takes place in “/tmp/test_inotify” file*/
// Modified from: http://www.thegeekstuff.com/2010/04/inotify-c-program-example/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )

int main( )
{
  int length, i = 0;
  int fd;
  int wd;
  char buffer[EVENT_BUF_LEN];

  /*creating the INOTIFY instance*/
  fd = inotify_init();
  /*checking for error*/
  if ( fd < 0 ) {
    perror( "inotify_init error" );
  }

  /* adding the “/tmp/test_inotify” test into watch list. Here, 
   * the suggestion is to validate the existence of the 
   * directory before adding into monitoring list.
   */
  wd = inotify_add_watch( fd, "/tmp/test_inotify", IN_CREATE | IN_DELETE | IN_ACCESS | IN_MODIFY | IN_OPEN );

  /* read to determine the event change happens on “/tmp/test_inotify” file. 
   * Actually this read blocks until the change event occurs
   */ 
  length = read( fd, buffer, EVENT_BUF_LEN ); 
  /* checking for error */
  if ( length < 0 ) {
    perror( "read" );
  }  

  /* actually read return the list of change events happens. 
   *  Here, read the change event one by one and process it accordingly.
   */
  while ( i < length ) {
    struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
    if( event->len == 0) {
      // For a single file watching, the event->name is empty, and event->len = 0
      printf(" Single file watching event happened\n");
    } else if ( event->len ) {
      if ( event->mask & IN_CREATE ) {
        if ( event->mask & IN_ISDIR ) {
          printf( "New directory %s created.\n", event->name );
        } else {
          printf( "New file %s created.\n", event->name );
        }
      } else if ( event->mask & IN_DELETE ) {
        if ( event->mask & IN_ISDIR ) {
          printf( "Directory %s deleted.\n", event->name );
        } else {
          printf( "File %s deleted.\n", event->name );
        }
      } else if( event->mask & IN_ACCESS ) {
        if ( event->mask & IN_ISDIR ) {
          printf( "Directory %s accessed.\n", event->name );
        } else {
      printf(" File %s accessed. \n", event->name );
        }
      } else if( event->mask & IN_MODIFY ) {
        if ( event->mask & IN_ISDIR ) {
          printf( "Directory %s modified.\n", event->name );
        } else {
      printf(" File %s modified. \n", event->name );
        }
      } else if( event->mask & IN_OPEN ) {
        if ( event->mask & IN_ISDIR ) {
          printf( "Directory %s opened.\n", event->name );
        } else {
      printf(" File %s opened. \n", event->name );
        }
      } else {
    printf( "Directory or File is accessed by other mode\n");
      }
    }
    i += EVENT_SIZE + event->len;
  }

  /* removing the “/tmp/test_inotify” directory from the watch list. */
  inotify_rm_watch( fd, wd );

  /* closing the INOTIFY instance */
  close( fd );

}

When runing the above program. You could test it by create a file or directoy named /tmp/test_inotify.

A detailed explanation could be found here

Use read system call

If a file is open, and have read to the end of current file size. the read() system call will return 0. And if some writer wrote N bytes to this file later, and then the read() will just return min(N, buffersize).

So it works correctly for your circumstance. Following is an examples of the code.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

typedef int FD ;

int main() {
  FD filed = open("/tmp/test_inotify", O_RDWR );
  char buf[128];

  if( !filed ) {
    printf("Openfile error\n");
    exit(-1);
  }

  int nbytes;
  while(1) {
    nbytes = read(filed, buf, 16);
    printf("read %d bytes from file.\n", nbytes);
    if(nbytes > 0) {
      split_buffer_by_newline(buf); // split buffer by new line.
    }
    sleep(1);
  }
  return 0;
}

Reference

于 2013-05-24T00:28:45.083 回答
0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int
main()
{

    char            ch;
    FILE           *fp;
    long int        nbytes_read = 0;
    char            str       [128];
    int             j = 0;
    int             first_time = 1;
    memset(str, '\0', 128);
    fp = fopen("file.txt", "r");
    while (1) {
            if (first_time != 1) {
                    fp = fopen("file.txt", "r");
                    fseek(fp, nbytes_read, SEEK_SET);
                    sleep(10);

            }
            if (fp != NULL) {
                    while ((ch = fgetc(fp)) != EOF) {
                            if (ch == '\n') {
                                    str[j++] = ch;
                                    printf("%s", str);
                                    memset(str, '\0', 128);
                                    j = 0;
                            } else {
                                    str[j++] = ch;
                            }
                            nbytes_read++;


                    }
                    //printf("%ld\n", nbytes_read);
                    first_time = 0;
            }
            fclose(fp);
    }
    return 0;
}
于 2018-07-25T06:40:29.107 回答
-1

使用select可能是一个不错的选择,但如果您不想使用它,您可以在读取值之前添加少量毫秒的睡眠。

于 2013-05-24T00:15:45.977 回答
-1

您可以select()fileno(file)用作文件描述符。select将返回超时(如果您设置了超时)或当您可以从文件中读取时。

于 2013-05-24T00:02:00.887 回答