我需要能够从标准输入中读取字符串

时间:2019-04-10 18:28:11

标签: c scanf

我正在尝试从stdin中读取一些数据。这将是数字(任何数字位数),由空格分隔。问题是我事先不知道长度。我希望能够从stdin读取并使用它来操作某些东西,这一直重复到按下^ d为止。

#include <stdio.h>
#include <stdlib.h>

int 
main(){

char input[] = scanf("%s", &input);

for(int i=0; i<sizeof(&input);i++){

//Do something

}

}

这不起作用,但是如何更改使其起作用?

4 个答案:

答案 0 :(得分:0)

是一个例子。您需要添加malloc和realloc结果检查(为了简单起见,我没有这样做)

tnc

然后您拥有可以扫描的字符串。使用后释放内存

并将其转换为整数数组(因为您不知道结果数组的大小):

#include <stdio.h>
#include <stdlib.h>

#define CHUNK   32 

char *readline(void)
{
    size_t csize = CHUNK;
    size_t cpos = 0;
    char *str = malloc(CHUNK);
    int ch;
    while((ch = fgetc(stdin)) != '\n' && ch != '\r')
    {
        str[cpos++] = ch;
        if(cpos == csize)
        {
            csize += CHUNK;
            str = realloc(str, csize);
        }
    }
    str[cpos] = 0;
    return str;
}

关于malloc和realloc结果检查与内存释放的相同说明

答案 1 :(得分:0)

这里的主要问题是您事先不知道元素的数量,在这种情况下,您需要使用动态内存保留空间来存储元素,可以使用队列,也可以使用realloc ,也请避免以这种方式使用scanf,请始终限制字符串的长度:

char str[100];

scanf("%99s", str); /* buffer overflow protection */

并始终检查结果:

if (scanf("%99s", str) != 1) {
    /* something went wrong */
}

使用fgets(作为scanf的替代方式)和strtol将数据存储在队列中的示例:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

struct node {
    struct node *next;
    int data;
};

void *enqueue(struct node **root, int data)
{
    struct node *node;

    node = malloc(sizeof *node);
    if (node == NULL) {
        return NULL;
    }
    if (*root == NULL) {
        node->next = node;
    } else {
        node->next = (*root)->next;
        (*root)->next = node;
    }
    node->data = data;
    *root = node;
    return node;
}

int dequeue(struct node **root)
{
    struct node *node;
    int data = 0;

    node = *root;
    if (node != NULL) {
        node = node->next;
        data = node->data;
        if (*root == node) {
            *root = NULL;
        } else {
            (*root)->next = node->next;
        }
        free(node);
    }
    return data;
}

int main(void)
{
    struct node *node = NULL;
    char str[512];
    char *ptr;
    int data;

    ptr = fgets(str, sizeof str, stdin);
    if (ptr != NULL) {
        while (*ptr) {
            data = (int)strtol(ptr, &ptr, 10);
            if (!isspace(*ptr)) { // If we don't have a blank space
                break;            // exit the loop
            }
            enqueue(&node, data);
            ptr++;
        }
    }
    while (node != NULL) {
        data = dequeue(&node);
        printf("%d\n", data);
    }
    return 0;
}

输入

123 456 -789

输出

123
456
-789

答案 2 :(得分:0)

要读取(并存储)stdin中未知数量的字符,您有两个基本选项:

  1. 如果可用,请使用POSIX getline()将所有字​​符读入缓冲区(getline将根据需要重新分配内存以存储整行输入,包括* nul终止字符),或者< / li>
  2. 如果您需要编写真正的可移植代码,请使用mallocrealloc处理初始的内存分配和所需的重新分配。

您已经发现,动态内存分配起初可能有些令人生畏,但实际上并没有任何理由。无论如何,您只需分配一些初始大小的内存块,将起始地址分配给指针,然后将所需的任何内容存储在该块中,以跟踪所使用的内存。当使用的内存等于可用的内存时(即,当您填满分配的内存块时),您只需使用临时指针重新分配更多的内存,验证对realloc的调用是否成功,然后分配开始重新分配的内存块到原始指针,并在每次填满内存块时继续进行并重复该过程。

有多种方法可以使用getchar()这样的面向字符的输入函数,或者使用一些固定大小的缓冲区和fgets来一次读取固定数量的字符,从而进行读取。完全取决于您。对于单个字符,请避免使用scanf,这是不必要的。基础读取由文件系统缓冲,因此无论您选择哪种方式都不会降低性能。 (Linux提供了一个8192字节的读取缓冲区,大小为IO_BUFSIZ(现在为BUFSIZ,请参阅glibc/libio/stdio.h - #define BUFSIZ 8192,并且_IO_BUFSIZ更改为BUFSIZ glibc commit 9964a14579e5eef9)并且Windows证明了类似的512字节缓冲区)

关键是逐步进行,验证每个分配并按要求处理错误。您将临时指针与realloc一起使用,因为如果realloc失败,它将返回NULL,并且如果您将realloc的返回值分配给原始指针,则将覆盖该地址NULL创建原始内存块,以创建内存泄漏。通过使用临时指针,如果realloc失败,则仍可以通过原始指针访问现有数据。

例如,要将当前分配的buffer的大小加倍,将当前分配的大小为buffersize,您可以天真地做:

buffer = realloc (buffer, 2 * buffersize);  /* wrong - potential memory leak */
if (buffer == NULL) {           /* validate reallocation */
    perror ("realloc-buffer");  /* output error message */
    /* handle error */
}
buffersize *= 2;    /* increment buffersize */

相反,您会这样做:

void *tmp = realloc (buffer, 2 * buffersize);  /* use a temporary pointer */
if (tmp == NULL) {              /* validate reallocation */
    perror ("realloc-buffer");  /* output error message */
    /* handle error, buffer still points to original block */
}
buf = tmp;
buffersize *= 2;

通过一个最小的简单示例来了解其工作方式。下面的代码将使用可移植的stdingetchar()malloc使用可重新分配缓冲区大小的简单重新分配方案,从realloc读取一行未知大小的行填充缓冲区的时间。 (您可以随意增加任意数量,但避免为读取的每个字符重新分配,这会效率低下,缓冲区大小加倍,或者类似的增加可以最大程度地减少重新分配的次数)

#include <stdio.h>
#include <stdlib.h>

#define NCHR 8  /* initial number of characters to allocate */

int main (void) {

    int c;                      /* char to read from stdin */
    size_t  ndx = 0,            /* index/count of characters */
            nchr = NCHR;        /* number of characters allocated in buf */
    char *buf = malloc (nchr);  /* buffer allocated for nchr chars */

    if (buf == NULL) {          /* validate that allocation succeeds */
        perror ("malloc-buf");  /* otherwise handle error */
        return 1;               /* bail */
    }

    /* read chars from stdin until '\n' or EOF */
    while ((c = getchar()) != '\n' && c != EOF) {
        if (ndx == nchr - 1) {  /* check if reallocation is needed */
            void *tmp = realloc (buf, 2 * nchr);    /* double buf size */
            if (tmp == NULL) {  /* validate realloc succeeds */
                perror ("realloc-buf"); /* handle error */
                break;          /* break don't bail, buf holds chars read */
            }
            buf = tmp;      /* assign newly sized block of mem to buf */
            nchr *= 2;      /* update nchr to new allocation size */
        }
        buf[ndx++] = c;     /* assign char to buf, increment index */
    }
    buf[ndx] = 0;       /* nul-terminate buffer */

    if (c == EOF)       /* if read stopped on EOF */
        putchar ('\n'); /* tidy up outputting \n */

    printf ("length : %zu\ncontent: %s\n", ndx, buf);

    free (buf);     /* don't forget to free what you allocate */
}

注意:EOF的检查将由Windows上的 Ctrl + d (或 Ctrl + z ),并在遇到其他情况时输出另外的'\n',否则下一个输出将在当前输入的末尾开始。另外请注意nchr - 1中的if (ndx == nchr - 1),确保始终有1个字符可用用于在循环退出后存储 nul-termination 。)

使用/输出示例

$ ./bin/getchar_dyn
1234 5678 9012 3456 7890
length : 24
content: 1234 5678 9012 3456 7890

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,valgrind是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

$ valgrind ./bin/getchar_dyn
==28053== Memcheck, a memory error detector
==28053== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28053== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==28053== Command: ./bin/getchar_dyn
==28053==
1234 5678 9012 3456 7890
length : 24
content: 1234 5678 9012 3456 7890
==28053==
==28053== HEAP SUMMARY:
==28053==     in use at exit: 0 bytes in 0 blocks
==28053==   total heap usage: 3 allocs, 3 frees, 56 bytes allocated
==28053==
==28053== All heap blocks were freed -- no leaks are possible
==28053==
==28053== For counts of detected and suppressed errors, rerun with: -v
==28053== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存,并且没有内存错误。

仔细检查一下,如果还有其他问题,请告诉我。

答案 3 :(得分:-2)

尝试先读取一行,然后读取该行中的整数

char *x ,*s ;
int d ;
while (fgets(input, sizeof(input), stdin)) {
x = input;
for (x = input; ; x = s) {
    d = strtol(x, &s, 10);
    if (x == s)
        break;        
  }
}