我需要能够从标准输入读取一个字符串[重复]

问题描述 投票:0回答:4

这个问题在这里已有答案:

我试图从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

}

}

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

c scanf
4个回答
0
投票

他的一个例子。你需要添加malloc和realloc结果检查(我不是为了简单起见)

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

然后你有你可以sscanf的字符串。使用后释放内存

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

int *tointegers(char *str, char *delimiters, size_t *size)
{
    int *array = malloc(CHUNK * sizeof(*array));
    char *ptr;

    *size = 0; 
    ptr = strtok (str, delimiters);
    while (ptr != NULL)
    {
        array[(*size)++] = atoi(ptr);
        if(*size == CHUNK)
        {
            array = malloc((*size + CHUNK) * sizeof(*array));
        }
        ptr = strtok (NULL, delimiters);
    }
    return array;
}

关于malloc&realloc结果检查和内存释放的相同评论


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

0
投票

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

  1. 如果可用,使用POSIX getline()将所有字符读入缓冲区(getline将根据需要重新分配内存以存储整行输入,包括* nul-terminating字符),或者
  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()mallocrealloc读取未知大小的行,使用重新分配方案,每次填充缓冲区时,简单地将缓冲区的大小加倍。 (您可以随意增加任何额外的数量,但避免重新分配每个读取的字符 - 效率低,缓冲区大小加倍或类似的增加可以最大限度地减少重新分配的次数)

#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将由Ctrl + d(或Windows上的Ctrl + z)生成,并在遇到时输出额外的'\n',否则您的下一个输出将在当前输入结束时开始。另请注意nchr - 1if (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)当它为no时可以释放它需要更久。

您必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认你释放了你分配的所有内存。

对于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)

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

仔细看看,如果您有其他问题,请告诉我。


-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;        
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.