不修改原始字符串指针的 strtok_r() 和 strsep() 的 C 字符串替代方案?

问题描述

我查看了 2 个 C 字符串函数,strtok_r() 和 strsep(),并注意到了两者 函数修改传入的原始字符串的位置。

是否还有其他不修改传入的原始字符串的 C 字符串函数

在我的应用中,原始字符串是动态分配的,所以我想释放原始字符串 解析完成后的字符串。

strtok_r() 示例

int main(){
    char * str = strdup("Tutorial and example");
    char* token;
    char* rest = str;
    
    printf("%s\n",rest);
    while ((token = strtok_r(rest," ",&rest)))
        printf("%s\n",token);
    printf("\n%s\n",str);
    return(0);
}

输出

Tutorial and example                                                                                                                                                        
Tutorial                                                                                                                                                                    
and                                                                                                                                                                         
example                                                                                                                                                                     
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                            
Tutorial                                                                                                                                                                          

在最后一行,我希望 str 指向未修改的 cstring“教程和示例”。

使用 strsep() 也会出现类似的输出

int main(){
    char * str = strdup("Tutorial and example");
    char* token;
    char* rest = str;

    printf("%s\n",rest); 
    while ((token = strsep(&rest," ")))
        printf("%s\n",token);
    if (rest != NULL)
        printf("%s\n",rest);
        
    printf("%s\n",str); 
    return(0);
}

谢谢。

解决方法

我认为您误解了strtok_r。它不会改变原始字符串的位置,而且,它也不能——函数不能改变传递给它的指针的值并使这个改变对调用代码可见。

它可以并且将会做的是修改字符串本身的内容,用nul-终止符替换标记。所以要回答你原来的问题:

在我的应用中,原始字符串是动态分配的,所以我 希望在解析完成后释放原始字符串。

您不必做任何特别的事情。完成后,您可以并且应该释放原始字符串。

您看到一个单词 Tutorial 被打印出来,只是因为下一个字符被替换为 nul-终止符并且 printf 停在那里。如果您要逐个字符地检查字符串,您将看到它在其他方面保持不变。

,

虽然上面提到的字符串函数改变了原来的字符串,但是指针str指向动态分配的内存,你可以用它来释放分配的内存。

如果您不想更改原始字符串,您可以使用标准 C 字符串函数 strspnstrcspn

例如

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

int main(void) 
{
    const char *s = "Tutorial and example";
    const char *separator = " \t";
    
    puts( s );
    
    for ( const char *p = s; *p; )
    {
        p += strspn( p,separator );
        
        const char *prev = p;
        
        p += strcspn( p,separator );
        
        int width = p - prev;
        
        if ( width ) printf( "%.*s\n",width,prev );
    }
    
    return 0;
}

程序输出为

Tutorial and example
Tutorial
and
example

使用这种方法,您可以为每个提取的子字符串动态分配内存。

例如

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

int main(void) 
{
    const char *s = "Tutorial and example";
    const char *separator = " \t";
    
    puts( s );
    
    size_t n = 0;
    char **a = NULL;
    int success = 1;
    
    for ( const char *p = s; success && *p; )
    {
        p += strspn( p,separator );
        
        if ( p - prev != 0 )
        {
            char *t = malloc( p - prev + 1 );
            
            if ( ( success = t != NULL ) )
            {
                t[p - prev] = '\0';
                memcpy( t,prev,p - prev );
            
                char **tmp = realloc( a,( n + 1 ) * sizeof( char * ) );
                
                if ( ( success = tmp != NULL ) )
                {
                    a = tmp;
                    a[n++] = t;
                }
                else
                {
                    free( t );
                }
            }
        }
    }
    
    for ( size_t i = 0; i < n; i++)
    {
        puts( a[i] );
    }

    for ( size_t i = 0; i < n; i++)
    {
        free( a[i] );
    }
    
    free( a );
    
    return 0;
}

程序输出与上图相同。

Tutorial and example
Tutorial
and
example