读取输入直到C中的分隔符(没有固定的最大长度)

读取输入直到C中的分隔符(没有固定的最大长度),c,C,我被要求从用户处读取一个重要符号(分隔符),并将其放入字符串中。我不应该询问需要多少行/字符,也不应该浪费内存空间 最好不要使用C语言中的内置包。我从零开始学习。所以 第一个问题:我应该使用指针还是字符数组? 请注意,我不知道我要阅读多长时间,我不能浪费内存 以下是我所做的: int main() { char s[100]; int line = 0; int i = 0; printf("type "); printf("\n"); scanf

我被要求从用户处读取一个重要符号(分隔符),并将其放入字符串中。我不应该询问需要多少行/字符,也不应该浪费内存空间

最好不要使用C语言中的内置包。我从零开始学习。所以

第一个问题:我应该使用指针还是字符数组?
请注意,我不知道我要阅读多长时间,我不能浪费内存

以下是我所做的:

int main() {
    char s[100];
    int line = 0;
    int i = 0;
    printf("type ");
    printf("\n");
    scanf("%s", &s[i++]);

    while (s[i] != '000') {
        if (s[i] == '\n') line++;
        i++;
        scanf("%s", &s[i]);
    } //end while
    s[i] = '\0';
    printf("\n");
    printf("lines %d", line);
    printf("\n");
    int j;
    while (s[j] != '\0') {
        printf("%s", s[j]);
        j++;
    }
    return 0;
} //end main

您应该将问题重命名为“如何读取未知长度的字符串”。。。 下面是一个这样做的例子。。有稍微更有效的方法,但我尽量保持简单

此代码将从
stdin
读取字符,并将其存储到
s
,根据需要分配内存。。最大为任意大小,直到它读取
delim
。。设置为一行三个
0
字符的字符串,达到EOF(文件结尾)或达到系统内存分配大小的限制

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

int main() {

    char c, *delim = "000";
    int i = 0, delimlen = strlen(delim);

    /* start out with 2 bytes of memory.. in reality the operating system */
    /* will allocate a lot more than that...                              */
    /* using calloc rather than malloc so that memory is initially nulled */
    char *s = calloc(2, 1);

    /* read characters one at a time from stdin */
    while((c = fgetc(stdin)) != EOF) {
        /* place character into the next position in the string array.. */
        /* and add 1 to i so it indexes the next position in the array  */
        s[i++] = c;
        /* add null terminator */
        s[i] = 0;

        /* did we get delimiter? */
        if(i >= delimlen && !strcmp(s+i-delimlen, delim))
            break;

        /* increase size of memory s points to, if necessary */
        s = realloc(s, i+2);
    }
    printf("we got input: %s", s);
    /* once you are TOTALLY done with s, free the memory so that it can be used again */
    free(s);
    exit(0);
}
#包括
#包括
#包括
int main(){
字符c,*delim=“000”;
int i=0,delimlen=strlen(delim);
/*从2字节的内存开始..实际上是操作系统*/
/*将分配更多的资源*/
/*使用calloc而不是malloc,以便内存最初为空*/
char*s=calloc(2,1);
/*从标准输入一次读取一个字符*/
而((c=fgetc(stdin))!=EOF){
/*将字符放入字符串数组中的下一个位置*/
/*并将1添加到i中,使其索引数组中的下一个位置*/
s[i++]=c;
/*添加空终止符*/
s[i]=0;
/*我们拿到定界符了吗*/
如果(i>=delimlen&&!strcmp(s+i-delimlen,delim))
打破
/*如有必要,将内存s点的大小增加到*/
s=realloc(s,i+2);
}
printf(“我们得到了输入:%s”,s);
/*一旦完全使用完s,请释放内存以便再次使用*/
免费的;
出口(0);
}
请注意,如果您的输入来自终端(键盘),而不是来自文件或管道,则在将输入传递到程序之前,它将等待您点击“回车”。因此,在此之前,您不会看到分隔符。如果这是一个问题,请查看以下内容:


虽然
getdelim
是为这种情况定制的,但在这种情况下,不使用预置函数来获得学习体验是一个非常好的选择。如果我理解该任务,您希望从文件中读取所有数据(或
stdin
),并且如果给定了一个替代分隔符(除正常的
'\n'
)则使用该字符作为行尾,以便分隔和计数行

要处理输入,您只需读取/存储数组中的每个字符(不是分隔符)(在下面的示例中,我们将使用静态数组,但如果需要,您可以分配/realloc)。如果读取了新的可选分隔符,则终止该行,增加行数,并移动到下一个字符

一种基本方法是:

#include <stdio.h>

#define MAXC 512

int main (int argc, char **argv) {

    int delim = argc > 1 ? *argv[1] : '\n';
    char s[MAXC] = {0};
    int c;
    size_t nchr = 0, lines = 0;

    /* for each char in input (stdin) */
    while ((c = getchar()) != EOF) {

        if (c == delim) {   /* if delim, store newline */
            s[nchr++] = '\n';
            lines++;
        }
        else if (c != '\n') /* store char */
            s[nchr++] = c;

        /* check (MAX - 2) to allow protection - see below */
        if (nchr == MAXC - 2) {
            fprintf (stderr, "warning: MAXC reached.\n");
            break;
        }
    }
    /* protect against no terminating delim */
    if (s[nchr-1] != delim) {
        s[nchr++] = '\n';
        lines++;
    }
    /* null-terminate */
    s[nchr] = 0;

    printf ("\nThere were '%zu' lines:\n\n", lines);
    printf ("%s\n", s);

    return 0;
}
示例输出

$ cat dat/captnjack_delim.txt
This is +a tale+
Of+ Captain Jack Sparrow+
A Pirate So Brave
On the +Seven Seas.
使用默认的
'\n'
作为delim

注意:您还可以调整条件测试以处理
'\n'
'
替换以满足您的需要。如果您正在读取文件,则将使用
fgetc
而不是
getchar
等。。如果您还需要
getdelim
示例,请告诉我

使用getdelim

使用带有动态内存分配的
getdelim
也可以完成同样的事情。注意:最初分配了
2
行的指针(
#define MAXL 2
),这将强制重新分配
,以处理超过2行的任何行。实际上,将其设置为合理预期的行数。(您希望尽可能减少分配/重新分配的数量。您也可以设置为1以强制每次分配新行,这样会降低效率)

开头包含的两个宏只需对
calloc
分配执行错误检查,并删除任何尾随的
回车
换行符
分隔符
。(如果愿意,可以将这些移动到函数)

注意:由于
getdelim
的工作方式,像
这是一个故事+
这样的分隔符将导致初始和嵌入的
换行符
包含在下一行中。您可以选择删除它们,但不要更改
s
的起始地址,因为它是由
getdelim
动态分配的。改为使用额外的指针和临时字符串

使用相同数据的简短示例如下:

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

#define MAXL 2

/* calloc with error check macro */
#define xcalloc(nmemb, size)       \
({  void *memptr = calloc((size_t)nmemb, (size_t)size);    \
    if (!memptr) {          \
        fprintf(stderr, "error: virtual memory exhausted.\n");  \
        exit(EXIT_FAILURE); \
    }       \
    memptr; \
})

/* remove trailing '\r' '\n' and delim macro */
#define rmcrlfdelim(str, delim)  \
({  char *p = (char *)str;  \
    int d = (int)delim; \
    for (; *p; p++) {}  \
    p--;    \
    for (; p > str && (*p == '\n' || *p == '\r' || *p == d); p--) \
        *p = 0, nchr--;  \
})

int main (int argc, char **argv) {

    int delim = argc > 1 ? *argv[1] : '\n';
    char **lines = NULL;
    char *s = NULL;
    ssize_t nchr = 0;
    size_t n = 0;
    size_t nlines = 0;
    size_t maxl = MAXL;
    size_t i = 0;

    lines = xcalloc (MAXL, sizeof *lines);

    /* for each segment of input (stdin) */
    while ((nchr = getdelim (&s, &n, delim, stdin)) != -1) {

        rmcrlfdelim (s, delim);         /* remove trailing \n \r delim  */
        lines[nlines++] = strdup (s);   /* allocate/copy s to lines     */

        if (nlines == maxl) {   /* realloc if needed */

            void *tmp = realloc (lines, maxl * 2 * sizeof *lines);
            if (!tmp) {
                fprintf (stderr, "error: realloc - memory exhausted.\n");
                exit (EXIT_FAILURE);
            }
            lines = (char **)tmp; /* below - set new pointers NULL */
            memset (lines + maxl, 0, maxl * sizeof *lines);
            maxl *= 2;
        }
    }
    free (s);   /* free mem allocated by getdelim */

    printf ("\nThere were '%zu' lines:\n\n", nlines);
    for (i = 0; i < nlines; i++)
        printf ("%s\n", lines[i]);

    for (i = 0; i < nlines; i++)    /* free allocated memory */
        free (lines[i]);
    free (lines);

    return 0;
}
使用
'+'
作为delim


首先:不使用制表符就可以正确缩进代码,因为普通人看不懂。你真是个好人,@chux@alk,开个玩笑d将编译器的警告级别设置为最大值。认真对待警告。您可能想学习如何使用调试器,以及如何使用Valgrind()之类的内存检查器。只是想一想——您的分隔符不应该是
int
而不是字符串吗?不。。分隔符(根据原始问题)是一行中三个“0”字符的序列。。这就是代码要查找的内容。如果它是一个整数delim=0,那么您如何知道只查找一个数字0、两个数字还是三个?“\000”是空字符的八进制文字,是的。但这不是我要找的分隔符。我正在寻找三个字符“000”的ASCII序列。我不知道为什么终止序列是
“000”
,但这就是问题的提问者所要求的。这是100%确定的,这就是为什么我用一个想法限定我的评论。。
$ ./bin/getchar_delim + <dat/captnjack_delim.txt

There were '6' lines:

This is
a tale
Of
 Captain Jack Sparrow
A Pirate So BraveOn the
Seven Seas.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXL 2

/* calloc with error check macro */
#define xcalloc(nmemb, size)       \
({  void *memptr = calloc((size_t)nmemb, (size_t)size);    \
    if (!memptr) {          \
        fprintf(stderr, "error: virtual memory exhausted.\n");  \
        exit(EXIT_FAILURE); \
    }       \
    memptr; \
})

/* remove trailing '\r' '\n' and delim macro */
#define rmcrlfdelim(str, delim)  \
({  char *p = (char *)str;  \
    int d = (int)delim; \
    for (; *p; p++) {}  \
    p--;    \
    for (; p > str && (*p == '\n' || *p == '\r' || *p == d); p--) \
        *p = 0, nchr--;  \
})

int main (int argc, char **argv) {

    int delim = argc > 1 ? *argv[1] : '\n';
    char **lines = NULL;
    char *s = NULL;
    ssize_t nchr = 0;
    size_t n = 0;
    size_t nlines = 0;
    size_t maxl = MAXL;
    size_t i = 0;

    lines = xcalloc (MAXL, sizeof *lines);

    /* for each segment of input (stdin) */
    while ((nchr = getdelim (&s, &n, delim, stdin)) != -1) {

        rmcrlfdelim (s, delim);         /* remove trailing \n \r delim  */
        lines[nlines++] = strdup (s);   /* allocate/copy s to lines     */

        if (nlines == maxl) {   /* realloc if needed */

            void *tmp = realloc (lines, maxl * 2 * sizeof *lines);
            if (!tmp) {
                fprintf (stderr, "error: realloc - memory exhausted.\n");
                exit (EXIT_FAILURE);
            }
            lines = (char **)tmp; /* below - set new pointers NULL */
            memset (lines + maxl, 0, maxl * sizeof *lines);
            maxl *= 2;
        }
    }
    free (s);   /* free mem allocated by getdelim */

    printf ("\nThere were '%zu' lines:\n\n", nlines);
    for (i = 0; i < nlines; i++)
        printf ("%s\n", lines[i]);

    for (i = 0; i < nlines; i++)    /* free allocated memory */
        free (lines[i]);
    free (lines);

    return 0;
}
$ ./bin/getdelim <dat/captnjack_delim.txt

There were '4' lines:

This is +a tale+
Of+ Captain Jack Sparrow+
A Pirate So Brave
On the +Seven Seas.
$ ./bin/getdelim + <dat/captnjack_delim.txt

There were '6' lines:

This is
a tale

Of
 Captain Jack Sparrow

A Pirate So Brave
On the
Seven Seas.
lines[ 0] : This is
lines[ 1] : a tale
lines[ 2] :
Of
lines[ 3] :  Captain Jack Sparrow
lines[ 4] :
A Pirate So Brave
On the
lines[ 5] : Seven Seas.