修改fflush(),保证在C中连续两次调用ungetc()

yua*_*ang 1 c stdio ungetc fflush getc

我是C初学者,我想连续两次调用ungetc()虽然我知道在常规C中它是不允许的.有人告诉我,我可以修改Fflush()做这项工作,但我不知道该怎么做.

这是我的代码,我Fflush唯一允许的代码,我ungetc()希望它允许两次.

#define altUngetc(c, fp) ((fp)->next > (fp)->buffer && (c) != EOF ? \
 *--(fp)->next = (c) : EOF)

int altGetc(ALT_FILE *fp) {
  if (fp->next == fp->buffer + fp->bufSize)
  altFflush(fp);

  return fp->flags & FILE_ATEOF ? EOF : *fp->next++;
}

int altFflush(ALT_FILE *fp) {
  int res;

  if (fp->fd < 0 || fp->flags & FILE_ATEOF)
     return EOF;
  if (fp->flags & FILE_READ) {                               
     res = read(fp->fd, fp->buffer, BUF_SIZE);               
     if (res == 0)                                           
        fp->flags |= FILE_ATEOF;                             
     fp->bufSize = res;                                      
     fp->next = fp->buffer;                                  
  }                                                          
  else {                                                     
     res = write(fp->fd, fp->buffer, fp->next - fp->buffer); 
     fp->next = fp->buffer;                                  
  }   
   return res < 0 ? EOF : 0;
}     
Run Code Online (Sandbox Code Playgroud)

344*_*442 5

正如评论中明智提到的那样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
Run Code Online (Sandbox Code Playgroud)

然后,在围栏的有趣的一面,来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;
}
Run Code Online (Sandbox Code Playgroud)

较小的xtdio库将允许您在传递xwrap()参数时执行尽可能多的ungets .每个XFILE都有一个带有ungotten字符的缓冲区.当你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;
}
Run Code Online (Sandbox Code Playgroud)

xtdio可以通过添加诸如xrewrap()扩展/缩小缓冲区大小之类的东西来进一步改进.

有一个更好的解决方案,它是重构您的代码,并遵循约定,这样您就不必ungetc()两次.xtdio只是一个概念证明,但不是好的代码,永远不会在实践中使用.这样,您就不必处理重写stdio.