繁体   English   中英

如何从C中的控制台读取一行?

[英]How to read a line from the console in C?

在 C 控制台程序中读取整行的最简单方法是什么 输入的文本可能具有可变长度,我们无法对其内容做出任何假设。

您需要动态内存管理,并使用fgets函数读取您的行。 但是,似乎没有办法看到它读取了多少个字符。 所以你使用 fgetc:

char * getline(void) {
    char * line = malloc(100), * linep = line;
    size_t lenmax = 100, len = lenmax;
    int c;

    if(line == NULL)
        return NULL;

    for(;;) {
        c = fgetc(stdin);
        if(c == EOF)
            break;

        if(--len == 0) {
            len = lenmax;
            char * linen = realloc(linep, lenmax *= 2);

            if(linen == NULL) {
                free(linep);
                return NULL;
            }
            line = linen + (line - linep);
            linep = linen;
        }

        if((*line++ = c) == '\n')
            break;
    }
    *line = '\0';
    return linep;
}

注意:永远不要使用gets! 它不做边界检查,可能会溢出你的缓冲区

如果您使用的是 GNU C 库或其他符合 POSIX 标准的库,您可以使用getline()并将stdin传递给文件流。

读取静态分配行的一个非常简单但不安全的实现:

char line[1024];

scanf("%[^\n]", line);

一个更安全的实现,没有缓冲区溢出的可能性,但有可能不读取整行,是:

char line[1024];

scanf("%1023[^\n]", line);

不是声明变量的指定长度与格式字符串中指定的长度之间的“差一”。 这是一件历史文物。

因此,如果您正在寻找命令参数,请查看 Tim 的答案。 如果您只想从控制台读取一行:

#include <stdio.h>

int main()
{
  char string [256];
  printf ("Insert your full address: ");
  gets (string);
  printf ("Your address is: %s\n",string);
  return 0;
}

是的,它不安全,你可以做缓冲区溢出,它不检查文件结尾,它不支持编码和很多其他东西。 其实我什至没有想过它是否做了任何这些事情。 我同意我有点搞砸了 :) 但是……当我看到“如何从 C 中的控制台读取一行?”之类的问题时,我认为一个人需要一些简单的东西,比如 get() 而不是 100 行代码像上面一样。 实际上,我认为,如果您在现实中尝试编写这 100 行代码,您会犯的错误比选择 get 时犯的错误要多;)

getline可运行示例

这个答案中提到getline ,但这里有一个例子。

它是POSIX 7 ,为我们分配内存,并在循环中很好地重用分配的缓冲区。

指针新手,请阅读: 为什么 getline 的第一个参数是指向指针“char**”而不是“char*”的指针?

主文件

#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char *line = NULL;
    size_t len = 0;
    ssize_t read = 0;
    while (1) {
        puts("enter a line");
        read = getline(&line, &len, stdin);
        if (read == -1)
            break;
        printf("line = %s", line);
        printf("line length = %zu\n", read);
        puts("");
    }
    free(line);
    return 0;
}

编译并运行:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out

结果:这在热敏电阻上显示:

enter a line

然后如果你输入:

asdf

然后按回车,显示如下:

line = asdf
line length = 5

其次是另一个:

enter a line

或者从管道到标准输入:

printf 'asdf\nqwer\n' | ./main.out

给出:

enter a line
line = asdf
line length = 5

enter a line
line = qwer
line length = 5

enter a line

在 Ubuntu 20.04 上测试。

glibc 实现

没有POSIX? 也许你想看看glibc 2.23 的实现

它解析为getdelim ,它是getline的简单 POSIX 超集,带有任意行终止符。

每当需要增加时,它都会将分配的内存加倍,并且看起来是线程安全的。

它需要一些宏观扩展,但你不可能做得更好。

您可能需要使用逐字符 (getc()) 循环来确保没有缓冲区溢出并且不会截断输入。

按照建议,您可以使用 getchar() 从控制台读取数据,直到返回行尾或 EOF,从而构建您自己的缓冲区。 如果您无法设置合理的最大行大小,则可能会动态增长缓冲区。

您还可以使用 fgets 作为一种安全的方式来获取一行作为 C 以空字符结尾的字符串:

#include <stdio.h>

char line[1024];  /* Generously large value for most situations */

char *eof;

line[0] = '\0'; /* Ensure empty line if no input delivered */
line[sizeof(line)-1] = ~'\0';  /* Ensure no false-null at end of buffer */

eof = fgets(line, sizeof(line), stdin);

如果您已经用尽了控制台输入或者操作由于某种原因失败,则返回 eof == NULL 并且行缓冲区可能保持不变(这就是将第一个字符设置为 '\\0' 很方便的原因)。

fgets 不会溢出 line[] 并且它会确保在成功返回时最后接受的字符之后有一个空值。

如果已到达行尾,终止 '\\0' 之前的字符将是一个 '\\n'。

如果在结尾 '\\0' 之前没有终止 '\\n',则可能是有更多数据或下一个请求将报告文件结束。 你必须再做一次 fgets 来确定哪个是哪个。 (在这方面,使用 getchar() 循环更容易。)

在上面的(更新的)示例代码中,如果成功 fgets 后 line[sizeof(line)-1] == '\\0' ,您就知道缓冲区已完全填满。 如果该位置以 '\\n' 开头,您就知道您很幸运。 否则,标准输入中有更多数据或文件结尾。 (当缓冲区没有完全填满时,您仍然可能位于文件末尾,并且当前行的末尾也可能没有 '\\n'。因为您必须扫描字符串以查找和/或消除字符串末尾之前的任何 '\\n'(缓冲区中的第一个 '\\0'),我倾向于首先使用 getchar()。)

做你需要做的事情来处理仍然有比你作为第一个块读取的数量更多的行。 动态增长缓冲区的示例可以与 getchar 或 fgets 一起使用。 有一些棘手的边缘情况需要注意(比如记住让下一个输入开始存储在 '\\0' 在缓冲区扩展之前结束前一个输入的位置)。

如何从C中的控制台读取一行?

  • 构建您自己的函数,是帮助您实现从控制台读取一行的方法之一

  • 我正在使用动态内存分配来分配所需的内存量

  • 当我们即将耗尽分配的内存时,我们尝试将内存大小增加一倍

  • 在这里,我使用循环来使用getchar()函数一个一个地扫描字符串的每个字符,直到用户输入'\\n'EOF字符

  • 最后,我们在返回行之前删除任何额外分配的内存

//the function to read lines of variable length

char* scan_line(char *line)
{
    int ch;             // as getchar() returns `int`
    long capacity = 0;  // capacity of the buffer
    long length = 0;    // maintains the length of the string
    char *temp = NULL;  // use additional pointer to perform allocations in order to avoid memory leaks

    while ( ((ch = getchar()) != '\n') && (ch != EOF) )
    {
        if((length + 1) >= capacity)
        {
            // resetting capacity
            if (capacity == 0)
                capacity = 2; // some initial fixed length 
            else
                capacity *= 2; // double the size

            // try reallocating the memory
            if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
            {
                printf("ERROR: unsuccessful allocation");
                // return line; or you can exit
                exit(1);
            }

            line = temp;
        }

        line[length] = (char) ch; //type casting `int` to `char`
        length++;
    }
    line[length + 1] = '\0'; //inserting null character at the end

    // remove additionally allocated memory
    if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
    {
        printf("ERROR: unsuccessful allocation");
        // return line; or you can exit
        exit(1);
    }

    line = temp;
    return line;
}
  • 现在您可以通过这种方式阅读整行:

     char *line = NULL; line = scan_line(line);

这是一个使用scan_line()函数的示例程序

#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions

char* scan_line(char *line)
{
    ..........
}

int main(void)
{
    char *a = NULL;

    a = scan_line(a); //function call to scan the line

    printf("%s\n",a); //printing the scanned line

    free(a); //don't forget to free the malloc'd pointer
}

样本输入:

Twinkle Twinkle little star.... in the sky!

样本输出:

Twinkle Twinkle little star.... in the sky!

许多人,像我一样,来到这个帖子,标题与搜索的内容相匹配,尽管描述的是关于可变长度的说法。 对于大多数情况,我们事先知道长度。

如果您事先知道长度,请尝试以下方法:

char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read

来源: https//www.tutorialspoint.com/c_standard_library/c_function_fgets.htm

在 BSD 系统和 Android 上,您还可以使用fgetln

#include <stdio.h>

char *
fgetln(FILE *stream, size_t *len);

像这样:

size_t line_len;
const char *line = fgetln(stdin, &line_len);

line不是空终止,最后包含\\n (或您的平台正在使用的任何内容)。 在流上的下一个 I/O 操作之后它变得无效。

前段时间我遇到了同样的问题,这是我的解决方案,希望它有所帮助。

/*
 * Initial size of the read buffer
 */
#define DEFAULT_BUFFER 1024

/*
 * Standard boolean type definition
 */
typedef enum{ false = 0, true = 1 }bool;

/*
 * Flags errors in pointer returning functions
 */
bool has_err = false;

/*
 * Reads the next line of text from file and returns it.
 * The line must be free()d afterwards.
 *
 * This function will segfault on binary data.
 */
char *readLine(FILE *file){
    char *buffer   = NULL;
    char *tmp_buf  = NULL;
    bool line_read = false;
    int  iteration = 0;
    int  offset    = 0;

    if(file == NULL){
        fprintf(stderr, "readLine: NULL file pointer passed!\n");
        has_err = true;

        return NULL;
    }

    while(!line_read){
        if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
            fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
            free(tmp_buf);

            break;
        }

        if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
            line_read = true;

        offset = DEFAULT_BUFFER * (iteration + 1);

        if((buffer = realloc(buffer, offset)) == NULL){
            fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
            free(tmp_buf);
            has_err = true;

            return NULL;
        }

        offset = DEFAULT_BUFFER * iteration - iteration;

        if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
            fprintf(stderr, "readLine: Cannot copy to buffer\n");
            free(tmp_buf);
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        free(tmp_buf);
        iteration++;
    }

    return buffer;
}

像这样的东西:

unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer
{
    char * strbfr;
    int c;
    unsigned int i;
    i = 0;
    strbfr = (char*)malloc(sizeof(char));
    if(strbfr==NULL) goto error;
    while( (c = getchar()) != '\n' && c != EOF )
    {
        strbfr[i] = (char)c;
        i++;
        strbfr = (void*)realloc((void*)strbfr,sizeof(char)*(i+1));
        //on realloc error, NULL is returned but original buffer is unchanged
        //NOTE: the buffer WILL NOT be NULL terminated since last
        //chracter came from console
        if(strbfr==NULL) goto error;
    }
    strbfr[i] = '\0';
    *pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer
    return i + 1; 
    error:
    *pStrBfr = strbfr;
    return i + 1;
}

从控制台读取一行的最好和最简单的方法是使用 getchar() 函数,您将一次在数组中存储一个字符。

{
char message[N];        /* character array for the message, you can always change the character length */
int i = 0;          /* loop counter */

printf( "Enter a message: " );
message[i] = getchar();    /* get the first character */
while( message[i] != '\n' ){
    message[++i] = getchar(); /* gets the next character */
}

printf( "Entered message is:" );
for( i = 0; i < N; i++ )
    printf( "%c", message[i] );

return ( 0 );

}

这是一个最小的实现,好处是它不会保留 '\\n',但是为了安全起见,你必须给它一个大小来读取:

#include <stdio.h>
#include <errno.h>

int sc_gets(char *buf, int n)
{
    int count = 0;
    char c;

    if (__glibc_unlikely(n <= 0))
        return -1;

    while (--n && (c = fgetc(stdin)) != '\n')
        buf[count++] = c;
    buf[count] = '\0';

    return (count != 0 || errno != EAGAIN) ? count : -1;
}

测试:

#define BUFF_SIZE 10

int main (void) {
    char buff[BUFF_SIZE];

    sc_gets(buff, sizeof(buff));
    printf ("%s\n", buff);

    return 0;
}

注意:您只能在 INT_MAX 内找到您的线路返回,这已经足够了。

这个函数应该做你想做的:

char* readLine( FILE* file )
 {
 char buffer[1024];
 char* result = 0;
 int length = 0;

 while( !feof(file) )
  {
  fgets( buffer, sizeof(buffer), file );
  int len = strlen(buffer);
  buffer[len] = 0;

  length += len;
  char* tmp = (char*)malloc(length+1);
  tmp[0] = 0;

  if( result )
   {
   strcpy( tmp, result );
   free( result );
   result = tmp;
   }

  strcat( result, buffer );

  if( strstr( buffer, "\n" ) break;
  }

 return result;
 }

char* line = readLine( stdin );
/* Use it */
free( line );

我希望这有帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM