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

时间:2016-03-13 06:32:22

标签: c stdio fflush getc ungetc

我是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;
}     

2 个答案:

答案 0 :(得分:5)

正如评论中明智提到的,you should probably first learn to work with the rules instead of trying to break them。但是,我们在这里回答这个问题,这意味着违反规则!请注意,fflush()setbuf()setvbuf()在此处的工作原因不同。

首先,至少需要四个自定义功能。一个用于创建&#34;代理缓冲区&#34;与文件相关(在fopen()之后调用),一个用于销毁它(在fclose()之前调用),一个用于实际取消注册(替换为ungetc(),另一个用于检索来自文件的char(替换fgetc()。不幸的是,这意味着在流上执行fscanf()fflush()等等会产生糟糕和丑陋的结果。你必须重写所有stdio

首先,让我们调用所有新内容xtdio(&#34;扩展stdio&#34;),首先来{{1} } ...

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()都有一个带有未标记字符的缓冲区。当你XFILE时,它会首先检查缓冲区中是否存在某些内容并检索它。否则,它会回退到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

答案 1 :(得分:0)

如果您知道如何实现int堆栈,则可以创建自己的ungetc()函数。只需将ungetc()的调用替换为myungetc()(等),如果该堆栈具有值,则弹出它们而不是从getc()读取。无论何时你想要取消,只需按照你读取它们的相反顺序将值推送到堆栈。

我最近的一个项目的例子:

/* stack.h */
#ifndef _STACK_H_
#define _STACK_H_
typedef struct {
  int * vals;
  int currsize;
  int maxsize;
} stack;

int stack_init(stack * this, int size);
void stack_destroy(stack * this);

void push(stack * this, int val);
int pop(stack * this);
int isempty(stack * this);
int isfull(stack * this);
int size(stack * this);
int maxsize(stack * this);
#endif

/* stack.c */
#include <stdlib.h>
#include "stack.h"
#define THIS (this)
#define VALS (this->vals)
#define CURRSIZE (this->currsize)
#define MAXSIZE (this->maxsize)
int stack_init(stack * this, int size) {
  VALS = malloc(sizeof(int)*size);
  CURRSIZE = 0;
  MAXSIZE = size;
  if (!VALS) {
    return 1; /* alloc fail */
  }
  return 0; /* successful init */
}
void stack_destroy(stack * this) {
  free(VALS);
}

void push(stack * this, int val) {
  if (isfull(THIS)) {
    return;
  }
  VALS[CURRSIZE++] = val;
}
int pop(stack * this) {
  if (isempty(THIS)) {
    return 0;
  }
  return VALS[--CURRSIZE];
}
int isempty(stack * this) {
  return (CURRSIZE == 0);
}
int isfull(stack * this) {
  return (CURRSIZE == MAXSIZE);
}
int size(stack * this) {
  return CURRSIZE;
}
int maxsize(stack * this) {
  return MAXSIZE;
}
#undef THIS
#undef VALS
#undef CURRSIZE
#undef MAXSIZE

/* main.c */
#include <stdlib.h>
#include <stdio.h>
#include "stack.h"

int stgetc(FILE * stream, stack * pushed) { /* The getc() equivalent */
  if (isempty(pushed)) {
    return getc(stream);
  } else {
    return pop(pushed);
  }
}

int stpush(int val, stack * pushed) { /* The ungetc() equivalent */
  if (isfull(pushed)) {
    return 1;
  } else {
    push(pushed,val);
    return 0;
  }
}

int main(int argc, char ** argv) {
  /* startup code, etc. */
  stack pushbuf; /* where the pushback will be stored */
  stack_init(&pushbuf, 32) /* 32 = maximum number of ungetc calls/characters we can push */
  FILE * in = fopen("/some/file","r");
  /* file read */
  int readchar;
  while ((readchar = stgetc(in,pushbuf)) != EOF) {
    /* do stuff */
    stpush(readchar,pushbuf); /* oops, read too much! */
  }
  fclose(&in); /* close file */
  stack_destroy(&pushbuf); /* clean up our buffer */
  /* ... */
}

(我为文字墙道歉,但不可能有更短的例子) 考虑到您似乎正在处理文件,应该可以fseek()向后,虽然这对两个文件和stdin都有效。