Модифицировать fflush(), которая гарантирует вызов ungetc() дважды подряд в C

Я новичок в 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 ответа

Как мудро упоминается в комментариях, 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()параметр. каждый 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 это просто подтверждение концепции, но это не хороший код, и никогда не будет использоваться на практике. Таким образом, вам не придется иметь дело с переписыванием stdio,

Если вы знаете, как реализовать 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,

Другие вопросы по тегам