You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言提取子字符串:如何从含空字符的字符串拆分指定子串

从含空字符的字节序列中提取子串并生成字符串数组

问题描述

如何从类似\0\0bon\0\0jour\0的字节序列中提取出"bon"和"jour",并整理成[0] = bon、[1] = jour、[2] = NULL(数组结束标记)的字符串数组?

原尝试代码

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

char    **ft_split(char const *s, char c)
{
    char    *s1;
    char    *tmp;
    char    **res;
    int     i;
    size_t  len = 100 /* how can I guess the size ? */;

    s1 = strdup(s);
    res = malloc(len + 1);
    if (!res)
        return (res);
    i = 0;
    while (*s1)
    {
        tmp = strchr(s1, c) + 1;
        if (!tmp)
            break ;
        if (*tmp)
            res[i++] = strdup(tmp);
        s1 = tmp + 1;
    }
    res[i] = 0;
    return (res); 
}

int     main(void)
{
    char    **res = ft_split("\0bon\0jour", '\0');

    int i = 0;
    while (res[i])
        printf("%s\n", res[i++]);
    
    i = 0;
    while (res[i])
        free(res[i++]);
    free(res);
    return (0);
}

原代码存在的问题

  1. strdup无法处理含空字符的序列:strdup会在遇到第一个\0时停止复制,原输入"\0bon\0jour"只会被复制为空字符串,后续处理完全失效。
  2. 硬编码数组长度不合理:len=100是固定值,无法适配实际子串数量,要么浪费内存,要么内存不足。
  3. 字符串函数的局限性:strchr、while(*s1)这类操作都以\0为结束标志,无法遍历到序列中\0之后的内容。
  4. 非法访问风险:如果strchr返回NULL,tmp = strchr(...) +1会导致指针越界。

修正方案

要处理含\0的字节序列,必须明确序列的总长度(因为常规字符串函数无法自动获取),分两步完成:先统计有效子串数量,再分配内存并提取子串。

修正后的代码

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

char    **ft_split(const unsigned char *s, char c, size_t total_len)
{
    char    **res;
    int     count = 0;
    size_t  i = 0;

    // 第一步:统计有效非空子串的数量
    while (i < total_len)
    {
        // 跳过连续的分隔符
        while (i < total_len && s[i] == c)
            i++;
        if (i >= total_len)
            break;
        // 定位当前子串的结束位置
        while (i < total_len && s[i] != c)
            i++;
        count++;
    }

    // 分配字符串数组内存(+1用于存放NULL结束标记)
    res = malloc(sizeof(char *) * (count + 1));
    if (!res)
        return NULL;

    // 第二步:提取每个有效子串
    i = 0;
    int idx = 0;
    while (i < total_len)
    {
        // 跳过连续分隔符
        while (i < total_len && s[i] == c)
            i++;
        if (i >= total_len)
            break;
        size_t start = i;
        // 找到子串结束位置
        while (i < total_len && s[i] != c)
            i++;
        // 为当前子串分配内存并复制内容
        res[idx] = malloc(sizeof(char) * (i - start + 1));
        if (!res[idx])
        {
            // 内存分配失败,释放已分配的资源
            while (idx > 0)
                free(res[--idx]);
            free(res);
            return NULL;
        }
        strncpy(res[idx], (const char *)&s[start], i - start);
        res[idx][i - start] = '\0'; // 手动添加字符串结束符
        idx++;
    }
    res[idx] = NULL; // 标记数组结束
    return res;
}

int     main(void)
{
    // 用数组存储字节序列,避免字符串字面量自动截断
    unsigned char data[] = {'\0', '\0', 'b', 'o', 'n', '\0', '\0', 'j', 'o', 'u', 'r', '\0'};
    size_t data_len = sizeof(data) / sizeof(data[0]);

    char    **res = ft_split(data, '\0', data_len);

    if (res)
    {
        int i = 0;
        while (res[i])
        {
            printf("[%d] = %s\n", i, res[i]);
            i++;
        }
        // 释放内存,避免泄漏
        i = 0;
        while (res[i])
            free(res[i++]);
        free(res);
    }
    return 0;
}

代码关键点说明

  • 使用数组存储原始序列:避免字符串字面量在第一个\0处自动截断,确保能访问到所有字节。
  • 传入总长度参数:通过total_len明确要处理的字节数,实现完整遍历。
  • 两次遍历逻辑:第一次统计子串数量,确保内存分配精准;第二次提取子串,逐个复制到数组中。
  • 内存安全处理:子串分配失败时,释放已分配的所有内存,避免泄漏。
  • 手动添加结束符:复制子串后手动添加\0,确保每个元素都是合法的C字符串。

内容的提问来源于stack exchange,提问作者Alex Rene

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 15:40:23