Модифицировать 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
,