在C程序中STDIN输入大小限制为1k

时间:2018-10-26 22:11:11

标签: c input stdin

使用 read 和其他功能,您可以指定想要读取的字节数,这可能是一个愚蠢的问题,但是从stdin读取时,我发现我只能在提示符下键入1024个字符,如果我键入1025字符,则不会写入,并且如果我希望读取该行(按ENTER键),我需要删除1024个字符,以便为“ \ n”留出空间。这仅在我的c程序中发生,而不在外壳中发生,那么是什么原因导致了此限制?

#include <unistd.h>
#include <stdio.h>

int main() {
    char buf[2048];
    int c;

    c = read(fileno(stdin), &buf, sizeof(buf));
    printf("%s\n", buf);

    return 0;
}

1 个答案:

答案 0 :(得分:0)

转移选择的注释以形成答案。

一般诊断

这是系统上终端驱动程序的属性,而不是程序或C库的属性。诸如Bash这样的现代shell不会读一行。他们会使用非规范输入来读取可用的字符。另请参见Canonical vs non-canonical terminal input

Barmar noted

  

请注意,read()不会在读取的输入中添加空终止符,但是printf()需要以空终止的字符串。

您可以告诉printf()要打印多少个字符,而不是添加一个空终止符:

printf("%.*s\n", c, buf);

但是,这与如何获得一长行输入的问题有关。

如果您使用开放源代码的操作系统,则可以修改终端驱动程序源代码并重新编译内核,以允许您在一行上键入超过1 KiB的内容,但任何不足的内容都不会工作。终端驱动程序施加了限制;您必须更改终端驱动程序才能更改该限制。如果您使用的是Linux,则可以在/proc文件系统中四处查看,以查看是否存在可以更改的动态配置参数(这样就不必重新编译内核,但必须更改设置)终端驱动程序的);我还没有听说过这种可能性。

如果您从浏览器中复制粘贴超过1 KiB且没有换行符的文本并将其粘贴到系统文件中,则限制可能会很麻烦。使用Vim之类的程序来管理它-它将终端置于非规范模式,因此不会遇到限制。

使用POSIX termios限制来自终端的输入

如果您希望程序从终端读取而没有行长(还需要进行行编辑,例如擦除或取消处理),则可以考虑使用此程序-slurp

/*
@(#)File:           $RCSfile: slurp.c,v $
@(#)Version:        $Revision: 1.3 $
@(#)Last changed:   $Date: 2018/10/28 17:14:24 $
@(#)Purpose:        Put terminal into non-canonical mode to slurp input
@(#)Author:         J Leffler
*/

/*TABSTOP=4*/

#include "posixver.h"
#include "stderr.h"
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

static const char optstr[] = "a:ho:V";
static const char usestr[] = "[-hV][-a output | -o output]";
static const char hlpstr[] =
    "  -a output  Append to named file (creating it if necessary)\n"
    "  -h         Print this help message and exit\n"
    "  -o output  Output to named file (truncating it if it exists)\n"
    "  -V         Print version information and exit\n"
    ;

static struct termios saved = { 0 };
static bool sigint_enabled = false;
static bool sigquit_enabled = false;
static bool slurping = false;

static void reset_termios(void);
static void set_non_canonical(void);
static void sig_handler(int signum);
static void set_signal_handling(void);
static void slurp(int ofd, const char *filename);

#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
extern const char jlss_id_slurp_c[];
const char jlss_id_slurp_c[] = "@(#)$Id: slurp.c,v 1.3 2018/10/28 17:14:24 jonathanleffler Exp $";
#endif /* lint */

int main(int argc, char **argv)
{
    const char *filename = "standard output";
    int ofd = STDOUT_FILENO;
    int oflag = 0;

    err_setarg0(argv[0]);

    int opt;
    while ((opt = getopt(argc, argv, optstr)) != -1)
    {
        switch (opt)
        {
        case 'h':
            err_help(usestr, hlpstr);
            /*NOTREACHED*/
        case 'o':
        case 'a':
            if (ofd != STDOUT_FILENO)
            {
                err_remark("the -a and -o flags are mutually exclusive\n");
                err_usage(usestr);
            }
            oflag = (opt == 'o') ? O_TRUNC : O_APPEND;
            if ((ofd = open(optarg, O_WRONLY | O_CREAT | oflag, 0644)) < 0)
                err_syserr("failed to open file %s for writing: ", optarg);
            filename = optarg;
            break;
        case 'V':
            err_version("PROG", &"@(#)$Revision: 1.3 $ ($Date: 2018/10/28 17:14:24 $)"[4]);
            /*NOTREACHED*/
        default:
            err_usage(usestr);
            /*NOTREACHED*/
        }
    }

    if (optind != argc)
    {
        err_remark("unexpected file name options (first is '%s')\n", argv[optind]);
        err_usage(usestr);
    }

    set_non_canonical();
    if (slurping)
        set_signal_handling();
    slurp(ofd, filename);

    return 0;
}

static void reset_termios(void)
{
    tcsetattr(STDIN_FILENO, 0, &saved);
}

static void set_non_canonical(void)
{
    if (tcgetattr(STDIN_FILENO, &saved) == 0)
    {
        struct termios modified = saved;
        atexit(reset_termios);
        /*
        ** On macOS 10.14 (at least), if you don't reset ISIG, the
        ** signal characters are not transferred to the program, so
        ** you can't detect those signals.  With ICANON reset, they
        ** don't generate the signal either.  The code does not try
        ** to handle the suspend (^Z) key specially, nor any other
        ** keys than EOF, INTR, QUIT.
        */
        modified.c_lflag &= ~(ICANON | ISIG);
        modified.c_cc[VMIN] = 1;
        modified.c_cc[VTIME] = 0;
        tcsetattr(STDIN_FILENO, TCSANOW, &modified);
        slurping = true;
    }
}

static void sig_handler(int signum)
{
    reset_termios();
    _exit(128 + signum);
}

/* Almost worth a data structure and a loop, but not quite */
static void set_signal_handling(void)
{
    /* Simulate SIGINT and SIGQUIT */
    if (signal(SIGINT, SIG_IGN) != SIG_IGN)
    {
        (void)signal(SIGINT, sig_handler);
        sigint_enabled = true;
    }
    if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
    {
        (void)signal(SIGQUIT, sig_handler);
        sigquit_enabled = true;
    }
    /* Have program terminate when sent normal signals */
    if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
        (void)signal(SIGHUP, sig_handler);
    if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
        (void)signal(SIGTERM, sig_handler);
    if (signal(SIGPIPE, SIG_IGN) != SIG_IGN)
        (void)signal(SIGPIPE, sig_handler);
}

static void slurp(int ofd, const char *filename)
{
    char buffer[4096];
    int  nbytes;

    while ((nbytes = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0)
    {
        /* Simulate EOF and interrupt and quit signals */
        if (nbytes == 1 && slurping)
        {
            if (buffer[0] == saved.c_cc[VEOF])
                break;
            if (sigint_enabled && buffer[0] == saved.c_cc[VINTR])
                exit(128 + SIGINT);
            if (sigquit_enabled && buffer[0] == saved.c_cc[VQUIT])
                exit(128 + SIGQUIT);
        }
        if (write(ofd, buffer, nbytes) != nbytes)
            err_syserr("failed to write %d bytes to %s: ", nbytes, filename);
    }
}

我在GitHub上的SOQ(堆栈溢出问题)存储库中以libsoq中的文件stderr.cstderr.hposixver.h的形式使用了所使用的库代码。子目录。

这处理了那些为粗心的人所困的陷阱。尽最大努力在退出终端时将其重置回初始状态(“已知良好”)。它确实模拟了EOF,中断和退出键盘信号,但没有模拟常规的终端处理,例如擦除或杀死。

当标准输入不是终端时,使用此命令没有任何意义,但是代码也应处理该操作(它只是进行常规读取)。您可以将输出发送到标准输出(默认)或文件(-o file创建或截断文件,-a file附加或创建文件)。