gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 16:26:34 26 4
gpt4 key购买 nike

可能是一个愚蠢的问题,使用read和其他函数,您可以指定要读取的字节数,但是当从stdin读取时,我发现只能在提示符中输入1024个字符,如果我输入 1025 个字符,它不会被写入,如果我想要读取该行(按 ENTER 键),我需要删除 1024 个字符,以便为 '\n' 留出空间。这种情况仅发生在我的 c 程序中,而不是 shell 中,那么是什么导致了此限制?

#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;
}

最佳答案

传输选定的评论以形成答案。

一般诊断

这是系统上终端驱动程序的属性,而不是程序或 C 库的属性。现代 shell(例如 Bash)不读取任何一行;而是读取一行数据。他们使用非规范输入读取可用的字符。另请参阅Canonical vs non-canonical terminal input .

Barmar noted :

Note that read() doesn't add a null terminator to the input that it reads, but printf() expects a null-terminated string.

您可以告诉 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);
}
}

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

对于粗心的人来说,这可以解决大多数陷阱。当终端退出时,它会尽力将终端重置回初始(“已知良好”)状态。它确实模拟了 EOF、中断和退出键盘信号,但它不模拟常规终端处理,例如删除或终止。

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

关于c - C 程序中 STDIN 输入大小限制为 1k,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53016875/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com