【问题标题】:Taking Input as a String in C在 C 中将输入作为字符串
【发布时间】:2021-08-17 04:44:30
【问题描述】:

这是一个很大的问题,所以请花点时间阅读并提供答案。

我的问题是,我们如何在 C 中将输入作为字符串?

我们通常要求用户提供字符数,比如n,我们可以简单地声明为char str[n]。这一切都会很好。

但是,当我们通常声明像char str[100] 这样的大小时。但是如果我们提供一个长度为 20 的字符串,那么 80 个字节被浪费了,我们通常不希望这样,可以吗像这样声明。

如果用户给出一个输入 120 的字符串,那么我们的字符数组中只会存储 100 个字符,我们也不希望这样。

所以,基本上我们不知道用户可能输入什么。他输入一串长度,他的选择。

在上述情况下,我们使用scanfgets 获取输入,例如scanf("%s", str)scanf("%[^\n]%*c", str)scanf("%[^\n]s",str)gets(str) 等。

当我们使用 scanf 时,当我们输入一个长度为 5 的字符串时,当我们输入 6 个字符时,第 6 个字符不会被存储。

当我们使用 puts 时,当我们输入一个长度为 5 的字符串时,当我们给出 6 个字符时,第 6 个字符将存储在连续的字节中,但是当我们尝试打印时第 6 个字符不会显示.当我们输入 6 个字符时,它会给出一条消息,例如“检测到堆栈粉碎”。我们不知道还有什么其他数据,它可能会被覆盖。

上述案例是对是错,请您帮帮我?

现在,还有另一种声明 String 并将输入作为字符串的方法,我们可以使用指针,就像我们可以动态分配内存,然后在处理完字符串时释放内存。我们使用like,malloccallocrealloc 来分配内存,free 来释放内存。

我们可以声明为char* str = (char*)malloc(size*sizeof(char)),我们将输入作为scanf("%[^\n]s",str)。但在这里,我们也需要提供尺寸。如果我们不知道大小怎么办?如果用户提供的输入大于大小怎么办?

我们也可以像char* str = (char*)malloc(sizeof(char)) 这样声明。在这里,当我们输入一个长度为 5 的字符串时。字符串以连续字节的形式存储在堆中,但我们只分配了 1 个字节,我们输入的剩余 4 个字节以某种方式存储,这基本上是非法的内存访问,我们不能这样做,对吗?

上面提到的2种情况是一样的,这是对还是错?你能帮帮我吗?

我在 Zugzwang,国际象棋术语。请你帮助我好吗?有哪些方法可以在不指定大小的情况下声明字符串并获取输入?我们可以在不指定大小的情况下动态分配吗?声明字符串的方法有哪些?

【问题讨论】:

  • 如何为字符串输入创建一个缓冲区,该缓冲区对您的用例而言具有最大大小,例如char buf[4096]。然后,接受buf 的输入,然后接受strlen(buf) 的输入,并使用该值动态分配实际字符串。
  • 典型实现实际上读取到数组的大小,并将输入的其余部分作为垃圾丢弃。如果您希望某些东西能够存储任何大小的输入,您可以实现一个动态分配内存的函数,并继续读取输入并重新分配它以尽可能多地扩展字符串的内存空间,但最终您会必须设置一个限制,并丢弃无法读取的内容,因为内存空间不是无限的。
  • 嘿PHD,我会这样做,但是缓冲区不会自己在堆栈上占用空间,在函数结束后,空间会被清除,对吗?无论如何要明确删除内存,buf [4096]。
  • 嘿user3766054,我会检查一下

标签: arrays c string pointers input


【解决方案1】:

理论

一种解决方案是创建bufferslinked结构。

这样,每次缓冲区空间不足时,您只需为另一个缓冲区分配更多内存,并将它们链接在一起。这个缓冲区链表可以一直增长,直到输入完成。

输入完成后,为字符串分配一大块连续内存,然后遍历链接缓冲区列表并将数据复制到最后字符串。

最后,为链接缓冲区分配的内存释放

实际例子

读取任意长度的字符串可以这么简单:

    int main(int argc, char *argv[])
    {
        char *string = readLine(); //read arbitrary-length string
        printf("%s", string); //print string
        free(string); //dont forget to free the string!
        return 0;
    }

所以让我们自己创建readLine() 函数。

  1. 创建链接缓冲区结构:
    #define LINKEDBUFFER_SIZE 256
    
    struct SLinkedBuffer
    {
        char buffer[LINKEDBUFFER_SIZE];
        int idx;
        struct SLinkedBuffer *next;
    };

    typedef struct SLinkedBuffer LinkedBuffer;
    
    LinkedBuffer *newLinkedBuffer()
    {
        LinkedBuffer *result = (LinkedBuffer *) malloc(sizeof(LinkedBuffer));
        if (result == NULL)
        {
            printf("Error while allocating memory!\n");
            exit(1);
        }
        result->idx = 0;
        result->next = NULL;
        return result;
    }
  1. 利用我们刚刚定义的链接缓冲区创建一个读取函数:
    char *readLine()
    {
        char *result = NULL;
        size_t stringSize = 0;
        
        /* Read into linked buffers */
        LinkedBuffer *baseLinkedBuffer = newLinkedBuffer();
        LinkedBuffer *currentLinkedBuffer = baseLinkedBuffer;
        int currentChar;
        while ((currentChar = fgetc(stdin)) != EOF && currentChar != '\n')
        {
            if (currentLinkedBuffer->idx >= LINKEDBUFFER_SIZE)
            {
                currentLinkedBuffer->next = newLinkedBuffer();
                currentLinkedBuffer = currentLinkedBuffer->next;
            }
            currentLinkedBuffer->buffer[currentLinkedBuffer->idx++] = currentChar;
            stringSize++;
        }
        
        /* Copy to a consecutive string */
        int stringIndex = 0;
        result = malloc(sizeof(char) * (stringSize + 1));
        if (result == NULL)
        {
            printf("Error while allocating memory!\n");
            exit(1);
        }
        currentLinkedBuffer = baseLinkedBuffer;
        while (currentLinkedBuffer != NULL)
        {
            for (int i = 0; i < currentLinkedBuffer->idx; i++)
                result[stringIndex++] = currentLinkedBuffer->buffer[i];
            currentLinkedBuffer = currentLinkedBuffer->next;
        }
        result[stringIndex++] = '\0';
        
        /* Free linked buffers memory */
        while (baseLinkedBuffer != NULL)
        {
            currentLinkedBuffer = baseLinkedBuffer->next;
            free(baseLinkedBuffer);
            baseLinkedBuffer = currentLinkedBuffer;
        }
        
        return result;
    }

现在我们可以简单地使用readLine() 函数来读取任何字符串,如主函数所示!

【讨论】:

  • currentChar 应该是 int 而不是 char 才能存储 EOF
【解决方案2】:

在手册中,getline(3) 就是您要查找的内容。

   #include <stdio.h>

   ssize_t getline(char **restrict lineptr, size_t *restrict n,
                   FILE *restrict stream);

其中的一点文字:

getline() 从流中读取整行,将包含文本的缓冲区的地址存储到 *lineptr 中。缓冲区以 null 结尾,并包含换行符(如果找到)。

如果在调用之前 *lineptr 设置为 NULL 并且 *n 设置为 0,那么 getline() 将分配一个缓冲区来存储该行。即使 getline() 失败,用户程序也应释放此缓冲区。

或者,在调用 getline() 之前,*lineptr 可以包含一个指向 malloc(3) 分配的缓冲区的指针,大小为 *n 字节。如果缓冲区不够大,无法容纳该行,getline() 使用 realloc(3) 调整其大小,并根据需要更新 *lineptr 和 *n。

在任何一种情况下,调用成功后,*lineptr 和 *n 都会被更新以分别反映缓冲区地址和分配的大小。

所以,getlinemalloc 甚至 realloc 您提供的缓冲区。考虑到这一点,您可以编写这样的程序:

/* getline.c
 *
 */
#include <stdio.h>

int main(void)
{
    char *s = NULL;
    ssize_t n = 0;

    fprintf(stderr, "Line: ");
    getline(&s, &n, stdin);

    printf("Size: %zu\n", n);
    //printf("String: %s", s);
    
    /* @isrnick comment */
    free(s);

    return 0;
}

然后用这样的东西测试它:

$ make getline
$ python -c "print('A' * 2000000)" | ./getline
Size: 2097664
$

它会打印分配的缓冲区的大小。因为我们输入ENTER 来输入一些字符串,而ENTER 给我们\ngetline 应该没问题。


基本的通用`cat`程序:
/* gcat.c
 */

#include <stdio.h>

int main(int argc, char **argv)
{
    char *s;
    ssize_t n;
    FILE *fp = stdin;

    if (argc > 1) {
        if(!(fp = fopen(argv[1], "r"))) {
            perror("fopen");
            return -1;
        }
    }

    while(getline(&s, &n, fp) > 0) 
        printf("%s", s);


    /* @isrnick comment */
    free(s);

    return 0;
}

您可以使用以下任一方式调用它:

$ cat gcat.c | ./gcat

或者……

$ ./gcat gcat·c

【讨论】:

  • 注意:getline 不是标准的 C 函数,默认情况下可能不可用。
  • 应该释放动态分配的内存。
  • @isrnick 同意,但如果您是来自mainreturn,就像上面的示例一样,操作系统(至少是Linux)会为您解决这个问题。
  • @isrnick 将帖子编辑到free 缓冲区完成后。
  • 是的,操作系统通常会在进程结束时释放内存,但是,最好强迫自己让程序直接释放它,并且永远不要依赖操作系统来释放它,即使只是为了养成始终释放动态分配的内存的习惯,以免在实际需要时忘记这样做。
【解决方案3】:

此代码将帮助您获取不带任何长度的字符串

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    char *line = NULL;
    size_t len = 0;
    ssize_t read;
    read = getline(&line, &len, stdin);
    printf("%s",line);
    printf("%lu",strlen(line));
    free(line);
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-01
    • 2016-05-16
    • 2021-01-06
    • 1970-01-01
    相关资源
    最近更新 更多