正如MBlanc提到的那樣,使用read()
來實現您自己的緩衝就是這裏的方法。
下面是一個演示一般方法的程序。我不建議正是這種做,因爲:
這裏介紹的功能使用靜態變量,並且將只爲一個單一的文件工作,將無法使用一旦結束了。實際上,你需要爲每個文件設置一個單獨的struct
,並將每個文件的狀態存儲在那裏,每次將它傳遞到你的函數中。
這樣可以在將緩衝區中的某些數據從緩衝區中移除後,保留緩衝區。實際上,實施一個循環隊列可能是一個更好的方法,但基本用法是一樣的。
如果此處的輸出緩衝區大於內部緩衝區,它將永遠不會使用該額外空間。實際上,如果你遇到這種情況,你要麼調整內部緩衝區大小,要麼將內部緩衝區複製到輸出字符串中,然後返回並在返回之前嘗試第二次調用read()
。
但是實現所有這些會給示例程序增加太多的複雜性,這裏的一般方法將顯示如何完成任務。
爲了模擬延遲在接收輸入,主程序將管從下面的方案,該方案只是輸出幾次,有時用換行,有時並沒有和sleep()
S IN的輸出之間的輸出:
delayed_output.c
:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Here is some input...");
fflush(stdout);
sleep(3);
printf("and here is some more.\n");
printf("Yet more output is here...");
fflush(stdout);
sleep(3);
printf("and here's the end of it.\n");
printf("Here's some more, not ending with a newline. ");
printf("There are some more words here, to exceed our buffer.");
fflush(stdout);
return 0;
}
主程序:
buffer.c
:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/select.h>
#define INTBUFFERSIZE 1024
#define BUFFERSIZE 60
#define GET_LINE_DEBUG true
/* Prints a debug message if debugging is on */
void get_line_debug_msg(const char * msg, ...)
{
va_list ap;
va_start(ap, msg);
if (GET_LINE_DEBUG) {
vfprintf(stderr, msg, ap);
}
va_end(ap);
}
/*
* Gets a line from a file if one is available.
*
* Returns:
* 1 if a line was successfully gotten
* 0 if a line is not yet available
* -1 on end-of-file (no more input available)
*
* NOTE: This function can be used only with one file, and will
* be unusable once that file has reached the end.
*/
int get_line_if_ready(int fd, char * out_buffer, const size_t size)
{
static char int_buffer[INTBUFFERSIZE + 1] = {0}; /* Internal buffer */
static char * back = int_buffer; /* Next available space in buffer */
static bool end_of_file = false;
if (!end_of_file) {
/* Check if input is available */
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
struct timeval tv = {0, 0};
int status;
if ((status = select(fd + 1, &fds, NULL, NULL, &tv)) == -1) {
perror("error calling select()");
exit(EXIT_FAILURE);
}
else if (status == 0) {
/* Return zero if no input available */
return 0;
}
/* Get as much available input as will fit in buffer */
const size_t bufferspace = INTBUFFERSIZE - (back - int_buffer) - 1;
const ssize_t numread = read(fd, back, bufferspace);
if (numread == -1) {
perror("error calling read()");
exit(EXIT_FAILURE);
}
else if (numread == 0) {
end_of_file = true;
}
else {
const char * oldback = back;
back += numread;
*back = 0;
get_line_debug_msg("(In function, just read [%s])\n"
"(Internal buffer is [%s])\n",
oldback, int_buffer);
}
}
/* Write line to output buffer if a full line is available,
* or if we have reached the end of the file. */
char * endptr;
const size_t bufferspace = INTBUFFERSIZE - (back - int_buffer) - 1;
if ((endptr = strchr(int_buffer, '\n')) ||
bufferspace == 0 ||
end_of_file) {
const size_t buf_len = back - int_buffer;
if (end_of_file && buf_len == 0) {
/* Buffer empty, we're done */
return -1;
}
endptr = (end_of_file || bufferspace == 0) ? back : endptr + 1;
const size_t line_len = endptr - int_buffer;
const size_t numcopy = line_len > (size - 1) ? (size - 1) : line_len;
strncpy(out_buffer, int_buffer, numcopy);
out_buffer[numcopy] = 0;
memmove(int_buffer, int_buffer + numcopy, INTBUFFERSIZE - numcopy);
back -= numcopy;
return 1;
}
/* No full line available, and
* at end of file, so return 0. */
return 0;
}
int main(void)
{
char buffer[BUFFERSIZE];
FILE * fp = popen("./delayed_output", "r");
if (!fp) {
perror("error calling popen()");
return EXIT_FAILURE;
}
sleep(1); /* Give child process some time to write output */
int n = 0;
while (n != -1) {
/* Loop until we get a line */
while (!(n = get_line_if_ready(fileno(fp), buffer, BUFFERSIZE))) {
/* Here's where you could do other stuff if no line
* is available. Here, we'll just sleep for a while. */
printf("Line is not ready. Sleeping for five seconds.\n");
sleep(5);
}
/* Output it if we're not at end of file */
if (n != -1) {
const size_t len = strlen(buffer);
if (buffer[len - 1] == '\n') {
buffer[len - 1] = 0;
}
printf("Got line: %s\n", buffer);
}
}
if (pclose(fp) == -1) {
perror("error calling pclose()");
return EXIT_FAILURE;
}
return 0;
}
和輸出:
[email protected]:~/src/sandbox/buffer$ ./buffer
(In function, just read [Here is some input...])
(Internal buffer is [Here is some input...])
Line is not ready. Sleeping for five seconds.
(In function, just read [and here is some more.
Yet more output is here...])
(Internal buffer is [Here is some input...and here is some more.
Yet more output is here...])
Got line: Here is some input...and here is some more.
Line is not ready. Sleeping for five seconds.
(In function, just read [and here's the end of it.
Here's some more, not ending with a newline. There are some more words here, to exceed our buffer.])
(Internal buffer is [Yet more output is here...and here's the end of it.
Here's some more, not ending with a newline. There are some more words here, to exceed our buffer.])
Got line: Yet more output is here...and here's the end of it.
Got line: Here's some more, not ending with a newline. There are some
Got line: more words here, to exceed our buffer.
[email protected]:~/src/sandbox/buffer$
'select'文件描述符運行,但'fgets'。如果你想非阻塞I/O你可能不應該用fgets'FILE *' – 2014-11-05 00:40:08
工作; [見這裏](http://stackoverflow.com/questions/5351994/will-read-ever-block-after-select)信息 – 2014-11-05 00:41:10
這很少是一個好主意,你混合無緩衝的I/O('select()'在這裏)和緩衝I/O(這裏是'fgets()')在同一個文件上同時存儲相同的數據。 – alk 2014-11-05 05:43:40