正如评论中明智地提到的那样,you should probably first learn to work with the rules instead of trying to break them
. 但是,我们是来回答这个问题的,这意味着要打破规则!考虑到fflush()
,setbuf()
或setvbuf()
出于不同的原因在这里都不会起作用。
首先,至少需要四个自定义函数。一种是创建与文件相关的“代理缓冲区”(在之后调用fopen()
),一种是销毁它(在之前调用fclose()
,一种是执行实际取消获取(替换为,另一种是从文件ungetc()
中检索一个)(替换为.不幸的是,这意味着在流上执行,等...会产生糟糕和丑陋的结果。你必须重写所有的!char
fgetc()
fscanf()
fflush()
stdio
首先,让我们将所有的新东西都称为xtdio
(“扩展stdio
”),所以,首先来xtdio.h
...
#ifndef __XTDIO_H__
#define __XTDIO_H__
#include <stdio.h>
typedef struct
{
FILE *file;
char *buffer;
size_t buffer_size;
size_t buffer_usage;
size_t buffer_tail_offset;
} XFILE;
/* I know this is not the best of API design, but I need to be
* compatible with stdio's API.
*/
XFILE *xwrap(FILE *file, size_t max_ungets);
void xunwrap(XFILE *xfile);
int xgetc(XFILE *xfile);
int xungetc(int ch, XFILE *xfile);
#endif
然后,在栅栏有趣的一面,来了xtdio.c
......
#include <stdlib.h>
#include <stdio.h>
#include "xtdio.h"
/* Create a XFILE wrapper, along with its respective buffer
* of 'max_ungets' size, around 'file'.
*/
XFILE *xwrap(FILE *file, size_t max_ungets)
{
XFILE *xfile = malloc(sizeof(XFILE));
if(xfile == NULL)
return NULL;
xfile->file = file;
xfile->buffer = malloc(max_ungets);
if(xfile->buffer == NULL) {
free(xfile);
return NULL;
}
xfile->buffer_size = max_ungets;
xfile->buffer_usage = 0;
xfile->buffer_tail_offset = 0;
return xfile;
}
/* Undo what 'xwrap()' did.
*/
void xunwrap(XFILE *xfile)
{
free(xfile->buffer);
free(xfile);
}
/* Check if there's something in the XFILE's
* buffer, and return it. Otherwise, fallback
* onto 'fgetc()'.
*/
int xgetc(XFILE *xfile)
{
if(xfile->buffer_usage == 0)
return fgetc(xfile->file);
if(xfile->buffer_tail_offset == 0)
xfile->buffer_tail_offset = xfile->buffer_size - 1;
else
xfile->buffer_tail_offset--;
xfile->buffer_usage--;
return xfile->buffer[xfile->buffer_tail_offset];
}
/* Here's the interesting part! If there's room in the
* buffer, it puts 'ch' in its front. Otherwise, returns
* an error.
*/
int xungetc(int ch, XFILE *xfile)
{
if(xfile->buffer_usage == xfile->buffer_size)
return EOF; //TODO: Set errno or something
xfile->buffer[xfile->buffer_tail_offset++] = (char)ch;
xfile->buffer_tail_offset %= xfile->buffer_size;
xfile->buffer_usage++;
return ch;
}
小型xtdio
库将允许您执行传递给xwrap()
's 参数的尽可能多的 ungets。每个XFILE
都有一个带有未获取字符的缓冲区。当 you 时xgetc()
,它首先检查缓冲区中是否有东西并检索它。否则,它回退到fgetc()
. 示例用例...
#include <stdio.h>
#include <string.h>
#include "xtdio.h"
int main()
{
const char *my_string = "I just ungot this same long string in standard and compliant C! No more one-char limits on ungetc()!\n";
const size_t my_string_len = strlen(my_string);
XFILE *xtdin = xwrap(stdin, my_string_len);
if(xtdin == NULL) {
perror("xwrap");
return 1;
}
for(size_t i = my_string_len; i != 0; i--)
xungetc(my_string[i - 1], xtdin);
int ch;
while((ch = xgetc(xtdin)) != EOF)
putchar(ch);
xunwrap(xtdin);
return 0;
}
xtdio
可以通过添加诸如xrewrap()
扩展/缩小缓冲区大小之类的东西来进一步改进。
还有一个更好的解决方案,那就是重构你的代码,并遵循约定,这样你就不必ungetc()
两次了。xtdio
只是一个概念证明,但不是好的代码,永远不会在实践中使用。这样,您就不必处理重写stdio
。