STDIN ограничение размера ввода до 1 Кб в программе на Си - PullRequest
0 голосов
/ 27 октября 2018

Вероятно, глупый вопрос, с 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 голосов
/ 28 октября 2018

Передача выбранных комментариев для формирования ответа.

Общая диагностика

Это свойство драйвера терминала в вашей системе, скореечем программы или библиотеки C.Современные оболочки, такие как Bash, не читают ни одной строки;они читают символы по мере их появления, используя неканонический ввод.См. Также Канонический или неканонический вход терминала .

Бармар отмечено :

Обратите внимание, что read() не добавляет нулевой терминатор к входу, который он читает, но printf() ожидает строку с нулевым символом в конце.

Вместо добавления нулевого терминатора, вы можете сказать printf(), сколькосимволы для печати:

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

Это, однако, касается вопроса о том, как получить длинную строку ввода.

Если вы используете o / s с открытым исходным кодом, вы можетеизмените исходный код драйвера терминала и перекомпилируйте ваше ядро, чтобы вы могли набрать более 1 КиБ в одной строке, но все, что не так, не сработает.Терминальный драйвер накладывает ограничение;Вы должны изменить драйвер терминала, чтобы изменить это ограничение.Если вы работаете в Linux, вы можете просмотреть файловую систему /proc, чтобы увидеть, есть ли параметр динамической конфигурации, который вы можете изменить (поэтому вам не нужно перекомпилировать ядро, но вы должны изменить настройкитерминал водителя);Я не слышал, чтобы это было возможно.

Ограничение может быть неприятным, если вы копируете и вставляете более 1 КБ текста без перевода строки в браузере и хотите вставить его вфайл в вашей системе.Для управления используйте такую ​​программу, как 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 (Stack Overflow Questions) репозитории на GitHub в виде файлов stderr.c, stderr.h и posixver.h в подкаталоге libsoq .

Это касается большинства ловушек для неосторожных.Он делает все возможное, чтобы вернуть терминал в исходное состояние («хорошо известно»), когда он выходит.Он имитирует EOF, прерывает и завершает сигналы клавиатуры, но не симулирует обычную обработку терминала, такую ​​как стирание или уничтожение.

Не имеет смысла использовать это, когда стандартный ввод не является терминалом, нокод должен обрабатывать это тоже ОК (он просто выполняет нормальное чтение).Вы можете отправить вывод на стандартный вывод (по умолчанию) или в файл (-o file для создания или усечения файла, -a file для добавления или создания файла).

...