【问题标题】:Putchar and Getchar outputs everytime I hit enterPutchar 和 Getchar 每次我按 Enter 时都会输出
【发布时间】:2015-03-26 16:55:53
【问题描述】:

所以我正在做 Ritchie/Kernighan 编写的 C Programming Language 2nd Edition 中的练习。特别练习1.9。 “编写一个程序将其输入复制到输出,用一个空格替换每个字符串。”

我相信我的解决方案是正确的,但是每次我点击进入屏幕时都会输出我的行而不是等待 EOF。这是预期的行为吗?我只是使用他在书中介绍的功能和关键字。

如果我有多个换行符,我如何强制它等到我输入 eof 才能得到输出? Putchar 一次只处理一个字符,所以也许不可能。

还有什么是退格?他是指空格键只是一个空格吗?

/*
 Copys its input to its output, replacing each string with multiple blanks with one. 
 Input  - "I am    running." 
 Output - "I am running."

 */
#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{
    for (int c = getchar(); c != EOF; ){

        while (c == ' '){
            c = getchar();


            if (c != ' '){
                putchar(' ');
            }
        }

        putchar(c);
        c = getchar();
    }

    return 0;
}

改进版:

int _tmain(int argc, _TCHAR* argv[])
{

    int c;

    while ((c = getchar()) != EOF){

        while (c == ' '){
            c = getchar();
                if (c == EOF){
                break;
            }

            if (c != ' '){
                putchar(' ');
            }
        }

        putchar(c);
    }

    return 0;
}

【问题讨论】:

  • 你的逻辑有点乱,我并不是说这是一种侮辱。当您学习时,很容易陷入whiles、fors 和if 语句中。也许考虑一下如何重新组织循环,获取字符并检查它直到到达EOF
  • 我认为你的改进版更好。它更具可读性并且使用更少的代码。
  • 你对退格有什么问题,我在问题描述中没有看到,我目前没有 K&R 的副本。
  • 练习 1-10 状态编写一个程序将输入复制到输出,用 \t 替换每个制表符,用 \b 替换每个退格键,用 \\ 替换每个反斜杠。
  • 退格是字符char c = '\b'。由于您的输入正在由控制台进行行缓冲,因此您可能不会在输入中看到任何退格字符,因此我不确定他们想在这里做什么。

标签: c


【解决方案1】:

看看theory-behind-getchar-and-putchar-functions,它解释了为什么你在每一行之后都得到输出。

要强制程序在输出前等待 EOF,你需要使用某种缓冲区,看看print-multiple-lines-by-getchar-and-putchar

【讨论】:

  • 我查看了第二个链接,但作者还没有查看 fgets 或数组。我不认为作者打算让我们在程序实现中使用它们。也许我只是在问一些我需要等待的事情。如果你看1.5.1的文件副本,他的文件副本实现非常简单。
  • @runners3431,是的,在尝试进行缓冲之前,您应该了解数组。
  • @runners3431,是的,如果您没有检查过数组,那么您可能不需要像他们在第二个链接中所做的那样做任何事情。考虑到您到目前为止所涵盖的内容,您的解决方案可能很好。
【解决方案2】:

您的putchar() 按您希望的方式工作,因为stdin 是行缓冲的,这不是您应该理所当然的。您最好收集字符串,解析它们并将它们作为输出提供。

我会这样做:

#include <stdio.h>
#include <string.h>

#define BUFF_SIZE 2048

int main(void)
{

    char buff[2048];
    char *buff_temp = buff;
    char *buff_ptr = buff;
    unsigned int len = 0;

    while (fgets(buff_ptr, sizeof buff, stdin) != NULL) {
        len += strlen(buff_ptr) + 1;
        buff_ptr += strlen(buff_ptr);
    }
    buff_ptr = buff;

    while (--len) {
        while (*buff_ptr == ' ' && buff_ptr[1] == ' ') {
            --len;
            ++buff_ptr;
        }

       *buff_temp++ = *buff_ptr++;
    }
    printf("%s\n", buff);
}

Online compiler link

【讨论】:

  • 看起来不错。唯一的调整是捕获buflen = strlen (buff_ptr);,然后是len += buflen + 1; buff_ptr = buflen;,以避免重复调用strlen
【解决方案3】:

您的程序无法正确收集直到 EOF 之前的所有输入,然后立即将其全部回显。严格来说,逐行收集和回显输入甚至是不正确的——你得到这个结果只是因为你的标准输入是行缓冲的,这很常见,但不是必需的。它实际上的是逐个字符的回显。

相反,您需要在某种保留空间中记录输入字符,直到看到 EOF,然后回显该空间的全部内容。由于对输入的形式或大小没有任何限制,实现它的最简单方法可能是将输入缓冲在一个临时文件中,直到需要回显它为止。您也可以在内存中缓冲数据,但您需要注意预留足够的内存和/或根据需要动态增加缓冲区的大小。

“Backspace”是由退格键(不是空格键)生成的代码,通常被解释为删除输入位置之前的字符的信号。在 ASCII 中,即字符 8(十进制)。

【讨论】:

  • 谢谢,但他甚至没有检查过缓冲区、数组或指针。我只是想知道代码是否正确,根据我在书中的位置,比如第 17 页。
  • 就像我说的,不,你描述的问题不正确。
  • 所描述的问题似乎并不要求在输出之前必须缓冲所有输入。这似乎是@runners3431 的一个额外问题。如果您还没有覆盖缓冲区,那么这段代码似乎非常好。它会复制所有输入并删除多余的空格。
  • @Matt,通过“描述的问题”我指的是 OP 的问题,而不是 K&R 的文本。 OP 的代码不是 he 描述的问题的正确解决方案,这就是他所要求的。
【解决方案4】:

退格是一个字符,由键盘产生(通常有一个说“退格”的键)

当使用“cooked”输入时,退格非常方便在将输入行传递给程序之前对其进行编辑。 (当 enter/carriageReturn 被击中时,它被传递给程序。)

通常,当从键盘输入文本时,不会出现 EOF。但是,可以从键盘强制 EOF,(取决于操作系统)ctrl-z 或 ctrl-d

【讨论】:

    【解决方案5】:

    有很多方法可以解决这个问题。正如其他人正确显示的那样,如果您知道您的输入不会超过特定大小,您可以声明一个 static buffer 足以容纳您的数据。但是,如果您发现自己处于不知道需要读取多少数据的情况,那么您可以求助于 dynamically allocating 您的缓冲区,它使您能够增加(或 realloc)您的缓冲区的大小根据需要缓冲。以下提供了该方法。 (您可以将文本文件重定向到它作为测试)。

    此外,不清楚您是想跳过所有空白行,还是只是不想在EOF 之前看到输入。无论如何,如果您想跳过空白行,您可以在分配 p = line; 后在读取循环中添加一个简单的测试。

    /* skip blank lines */
    if (*p == '\n' || *p == '\r')
        continue;
    

    此示例使用getline 而不是fgets,这为您提供了两个优势。 (1) getline 如果你将它初始化为NULL ,它将为你分配行缓冲区; (2) 返回实际读取的字符数,无需调用strlen。如果您有任何问题,请告诉我:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void *realloc_void_on (void *p, size_t o, size_t n, size_t psz);
    
    int main (void) {
    
        char *line = NULL;      /* pointer to use with getline ()   */
        char *p = NULL;         /* pointer to parse getline return  */
        ssize_t nchr = 0;       /* actual chars read per-line       */
        size_t n = 0;           /* max chars to read (0 - no limit) */
        char *buff = NULL;      /* buffer to hold all lines read    */
        char *ep = NULL;        /* end pointer for buff             */
        size_t cbuffsz = 0;     /* current buffer size              */
        size_t nbuffsz = 0;     /* new buffer size                  */
        size_t offset = 0;      /* offset to orient end pointer     */
    
        /* read each line from stdin */
        while ((nchr = getline (&line, &n, stdin)) != -1)
        {
            p = line;           /* assign line address to pointer   */
    
            /* set required new buff size, realloc, set end ptr, update cbuffsz */
            nbuffsz += nchr + 1;
            buff = realloc_void_on (buff, cbuffsz, nbuffsz, sizeof *buff);
            ep = buff + offset;
            cbuffsz = nbuffsz;
    
            while (*p) {                            /* for each character in line       */
                *ep++ = *p;                         /* add char to buff, increment ep   */
                offset++;                           /* increment offset                 */
                if (*p == '\t' || *p == ' ') {      /* if space, or tab                 */
                    while (*p == '\t' || *p == ' ') /* read/discard following spaces    */
                        p++;
                }
                else
                    p++;                            /* if not space, increment pointer  */
            }
        }
    
        /* output complete buffer */
    #ifdef DEBUG
        printf ("\nBuffer:\n-----\n%s-----\n\n", buff);
    #else
        printf ("%s\n", buff);
    #endif
    
        /* free allocated memory */
        if (line) free (line);
        if (buff) free (buff);
    
        return 0;
    }
    
    /* reallocate memory for p of type size psz, from o to n.
    * accepts any pointer p, with current allocation o,
    * with the type size psz and reallocates memory to
    * n, intializing the new memory to zero and returning
    * a pointer to the newly allocated block of memory on
    * success, exit otherwise.
    */
    void *realloc_void_on (void *p, size_t o, size_t n, size_t psz)
    {
        void *tmp = realloc (p, n * psz);
    #ifdef DEBUG
        printf ("\n  reallocating %zu to %zu\n", o, n);
    #endif
        if (!tmp) {
            fprintf (stderr, "Error: pointer reallocation failure.\n");
            exit (EXIT_FAILURE);
        }
        p = tmp;
        memset (p + o, 0, (n - o) * psz);   /* memset new ptrs 0 */
    
        return p;
    }
    

    构建/编译

    没有 realloc 调试信息:

    gcc -Wall -Wextra -o buildbuf buildbuf.c
    

    使用 realloc DEBUG 信息(包括gdb 调试信息):

    gcc -Wall -Wextra -g -DDEBUG -o buildbuf buildbuf.c
    

    输入

    I am    running.
    

    输出

    I am running.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-22
      相关资源
      最近更新 更多