首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >修改fflush()以保证在C中连续调用ungetc()两次

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

Stack Overflow用户
提问于 2016-03-13 14:32:22
回答 2查看 293关注 0票数 1

我是一个C初学者,我想连续调用ungetc()两次,尽管我知道在普通的C中这是不允许的。有人告诉我,我可以修改Fflush()来做这项工作,但是我不知道怎么做。

这是我的代码,我的Fflush只允许一个ungetc(),我想让它允许两个。

代码语言:javascript
复制
#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;
}     
EN

回答 2

Stack Overflow用户

发布于 2016-03-13 16:44:43

正如评论中明智地提到的,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……

代码语言:javascript
复制
#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出现了.

代码语言:javascript
复制
#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()参数的尽可能多的unget。每个XFILE都有一个包含未获取字符的缓冲区。当您执行xgetc()时,它首先检查缓冲区上是否有东西并检索它。否则,它将回退到fgetc()。示例用例...

代码语言:javascript
复制
#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;
}

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

还有一个更好的解决方案,那就是重构你的代码,遵循的约定,这样你就不需要ungetc()两次了。xtdio只是一个概念证明,但不是好的代码,永远不应该在实践中使用。这样,您就不必重写stdio了。

票数 5
EN

Stack Overflow用户

发布于 2017-10-17 07:39:52

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

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

代码语言:javascript
复制
/* 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都有效。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35967293

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档