【问题标题】:Spliting a string into an array of strings completly dynamicly allocated将字符串拆分为完全动态分配的字符串数组
【发布时间】:2017-10-03 20:47:15
【问题描述】:

这个问题与this topic 非常接近,但我更喜欢这个解决方案提供的易懂性和我需要的指针说明。

所以我有一个数据文件,我从中得到了一个很长的字符数组。我想将此字符串拆分为一个数组,在每种情况下,一个字符串对应于该文件的一行。
我看到了解决方案,但它们都使用有限的数组,因为我不知道每行的长度,我真的需要动态分配它们,但我找不到行的长度,因为strtokdoesn't put一个空字符\0在每个字符串的末尾。

我现在得到的是这两种解决方案,但都不起作用:

int get_lines(char *file, char **lines) {
    int nb_lines = 0;
    char *token = strtok(file, "\n");
    for(int i = 0; token != NULL; i++) {
        token = strtok(NULL, "\n");
        nb_lines = i;
    }
    nb_lines++;

    lines = malloc((nb_lines + 1) * sizeof(char*));
    lines[nb_lines] = '\0';

    token = strtok(file, "\n");
    for(int i = 0; token != NULL; i++) {
        token = strtok(NULL, "\n");
        int nb_char = 0;
        for(int j = 0; token[j] != '\n'; j++) //This will cause SIGSEGV because strtok don't keep the '\n' at the end
            nb_char = j;
        nb_char++;
        token[nb_char] = '\0'; //This cause SIGSEGV because token's allocation finish at [nb_char-1]
        lines[i] = malloc(strlen(token) * sizeof(char)); //strlen cause SIGSEGV because I cannot place the '\0' at the end of token
        printf("%s", token); //SIGSEGV because printf don't find the '\0'
        lines[i] = token;
    }

    for(int i = 0; i < nb_lines; i++) {
        printf("%s", lines[i]); //SIGSEGV
    }

    return nb_lines;
}

所以你可以在上面看到我想要做什么以及为什么它不起作用的想法。

您将在下面看到我进行的另一次尝试,但我被困在同一点:

int count_subtrings(char* string, char* separator) {
    int nb_lines = 0;
    char *token = strtok(string, separator);
    for(int i = 0; token != NULL; i++) {
        token = strtok(NULL, separator);
        nb_lines = i;
    }
    return nb_lines + 1;
}

char** split_string(char* string, char* separator) {
    char **sub_strings = malloc((count_subtrings(string, separator) + 1) * sizeof(char*));
    for(int i = 0; string[i] != EOF; i++) {
        //How to get the string[i] lenght to malloc them ?
    }
}

我的文件很大,而且行也可能很大,所以我不想 malloc 另一个大小为 (strlen(file) + 1) * sizeof(char) 的表,以确保每行都不会 SIGSEGV,而且我也发现这个解决方案很脏,如果你们有其他想法,我会很高兴的。

(对不起英文错误,我不是很好)

【问题讨论】:

  • 您可以使用动态链表类型的数据结构。
  • 查看 realloc
  • 使用getline()逐行读取文件。将生成的行指针复制到char*数组中的下一个条目在每次调用getline()之前将行指针重置回NULL,建议char*数组动态分配,这样可以在/时调用realloc()如果数组已满。
  • 感谢@user3629249,但我将使用已检查的 awnser,因为我已经将文件加载到一个数组中,我想知道如何拆分它,即使您的 awnser 也可以正常工作。

标签: c arrays string dynamic-allocation


【解决方案1】:

您使用strtok 的方法有两个缺点:首先,strtok 修改了字符串,因此您只能将原始字符串传递一次。其次,它会跳过空行,因为它将线条的延伸作为单个标记分隔符..(我不知道这是否与您有关。)

您可以通过一次遍历字符串来计算换行符。为您的线阵列分配内存并进行第二次传递,在换行符处拆分字符串:

char **splitlines(char *msg)
{
    char **line;
    char *prev = msg;
    char *p = msg;

    size_t count = 0;
    size_t n;

    while (*p) {
        if (*p== '\n') count++;
        p++;
    }

    line = malloc((count + 2) * sizeof(*line));
    if (line == NULL) return NULL;

    p = msg;
    n = 0;
    while (*p) {
        if (*p == '\n') {
            line[n++] = prev;
            *p = '\0';
            prev = p + 1;
        }

        p++;
    }

    if (*prev) line[n++] = prev;
    line[n++] = NULL;

    return line;
}

我分配的行指针比换行数多两个:一个用于最后一行不以换行结尾的情况,另一个用于在末尾放置 NULL 标记,以便您知道在哪里你的数组结束。 (当然,您可以通过指向 size_t 的指针返回实际的行数。)

【讨论】:

  • 首先谢谢你的帮助,你的算法很干净,我很喜欢。我有几个问题,为什么你只使用size_t 变量来遍历数组? “通过指向size_t 的指针返回实际行数”是什么意思,这是如何工作的?如果 *p 是 0 ,为什么第二个 while (*p) 不中断?在line[n++] = prev; 中,n 在指令line[n] = prev; 之后递增? malloc() 的失败是我在所有代码中真正应该关心的吗?
  • (1) size_t 是无符号整数类型;标准库也将它用于不能为负数的事情,例如strlen 返回的值。如果您愿意,可以使用int。 (2)创建函数f(char *s, size_t *pn),然后在返回之前说if (pn) *pn = n;(我在那里犯了一个错误——返回之前的行应该是lin[n] = NULL,没有增量。)
  • (3) line[n++] = x 是典型的 C 习语。请记住,n 项的数组具有从 0 到 n - 1 的有效索引。项目n 是紧跟在有效范围之后的项目。附加到数组时,分配给该字段并增加计数。 (4) 是的,你应该。在当前代码中,调用函数应检查返回的指针是否为空。您也可以在分配失败时中止程序以获得快速解决方案。
  • 好的,谢谢,(1) 所以size_t 或unsigned int 只是一个偏好问题? (2) 我不知道你可以调用一个没有所有参数的函数。 (3) 我一直用for (int i=0; array[i] != '\0'; i++) array[i] = x 来遍历一个数组,while (array[i] != '\0') array[i++] = x 是一样的还是它有优势? (4) 那我会更加小心。 (5) 你忘了这个问题:如果*p 是0,为什么while (*p) 不中断?
  • 这个空间真的不适合回答很多长问题。 (1) 对于计数,首选无符号类型。 (2) 你错了;那将是另一个功能。 (3) 视情况而定。在我的情况下,我可以使用 for 循环。当指针有条件地或以不同的步幅前进时,使用while。 (5) 我不明白你的问题。 if (*p) 与 ´if (*p == '\0')` 相同。
【解决方案2】:

以下建议的代码:

  1. 干净编译
  2. (在堆大小限制内)不关心输入文件大小
  3. echo 是文件行的结果数组,双倍行距,只是为了表明它有效。对于单个间距,将 puts() 替换为 printf()

现在是代码

#include <stdio.h>   // getline(), perror(), fopen(), fclose()
#include <stdlib.h>  // exit(), EXIT_FAILURE, realloc(), free()


int main( void )
{
    FILE *fp = fopen( "untitled1.c", "r" );
    if( !fp )
    {
        perror( "fopen for reading untitled1.c failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, fopen successful

    char **lines = NULL;
    size_t availableLines = 0;
    size_t usedLines = 0;

    char *line = NULL;
    size_t lineLen = 0;
    while( -1 != getline( &line, &lineLen, fp ) )
    {
        if( usedLines >= availableLines )
        {
            availableLines = (availableLines)? availableLines*2 : 1;
            char **temp = realloc( lines, sizeof( char* ) * availableLines );
            if( !temp )
            {
                perror( "realloc failed" );
                free( lines );
                fclose( fp );
                exit( EXIT_FAILURE );
            }

            // implied else realloc successful

            lines = temp;
        }

        lines[ usedLines ] = line;
        usedLines++;
        line = NULL;
        lineLen = 0;
    }

    fclose( fp );

    for( size_t i = 0; i<usedLines; i++ )
    {
        puts( lines[i] );
    }

    free( lines );
}

鉴于以上代码在一个名为:untitled1.c 的文件中,以下是输出。

#include <stdio.h>   // getline(), perror(), fopen(), fclose()

#include <stdlib.h>  // exit(), EXIT_FAILURE, realloc(), free()





int main( void )

{

    FILE *fp = fopen( "untitled1.c", "r" );

    if( !fp )

    {

        perror( "fopen for reading untitled1.c failed" );

        exit( EXIT_FAILURE );

    }



    // implied else, fopen successful



    char **lines = NULL;

    size_t availableLines = 0;

    size_t usedLines = 0;



    char *line = NULL;

    size_t lineLen = 0;

    while( -1 != getline( &line, &lineLen, fp ) )

    {

        if( usedLines >= availableLines )

        {

            availableLines = (availableLines)? availableLines*2 : 1;

            char **temp = realloc( lines, sizeof( char* ) * availableLines );

            if( !temp )

            {

                perror( "realloc failed" );

                free( lines );

                fclose( fp );

                exit( EXIT_FAILURE );

            }



            // implied else realloc successful



            lines = temp;

        }



        lines[ usedLines ] = line;

        usedLines++;

        line = NULL;

        lineLen = 0;

    }



    fclose( fp );



    for( size_t i = 0; i<usedLines; i++ )

    {

        puts( lines[i] );

    }



    free( lines );

}

【讨论】:

    猜你喜欢
    • 2012-02-22
    • 2019-05-22
    • 2016-04-01
    • 2022-01-21
    • 1970-01-01
    • 2012-06-27
    相关资源
    最近更新 更多