【问题标题】:Working with strings in C that contain multiple Null characters在 C 中处理包含多个 Null 字符的字符串
【发布时间】:2013-12-17 09:17:23
【问题描述】:

我有一个 char[] 缓冲区,其中包含从内存中读取的数据,这意味着此缓冲区中有多个 Null,而不仅仅是一个结尾的 Null。

char *addr = (char *)memmem(buff, strlen(buff), needle, strlen(needle));

我需要处理这个字符串并搜索和替换某些内容。 为此,我想使用memmem(),但不幸的是,它不起作用,因为缓冲区中有多个 Null。

我尝试将缓冲区中的 Null 替换为我认为不会出现的字符(Bell ASCII 代码 \7),然后对其进行处理,但这不起作用并损坏了我的数据。

还有其他方法可以处理具有多个 Null 的字符串吗?

【问题讨论】:

  • 一个 C 字符串只有一个 \0 并且在它的末尾。包含任何值的 char 数组不是字符串,不能在其上使用 <string.h> 函数。
  • 请不要将memmem()的返回值投射到C中。

标签: c linux memory


【解决方案1】:

memmem 可以用来做。但是,由于您的数据包含嵌入的空值,因此您不能使用 strlen 来计算长度。您需要单独跟踪长度。

char *addr = (char *)memmem(buff, buffLen, needle, needleLen);

【讨论】:

    【解决方案2】:

    在您自己承认包含终止的 char 数组上使用 strlen 是行不通的。您应该意识到strlen 只是计算字符数,直到遇到零终止符。
    顺便说一句,strncat 也是这样工作的,所以你也不能使用这些功能。

    您可以做的是在手边保留一个int,您可以使用它来跟踪字符串的实际长度。
    解决此问题的另一种方法是编写自己的 d_strlen 函数,并始终确保缓冲区末尾有 两个 零终止字符:

    size_t d_strlen(const char *in)
    {
        size_t len = 0;
        while( !(in[len] == '\0' && in[len+1] == '\0' )) ++len;
        return len;
    }
    

    再说一遍:你的buffer 必须然后由 两个 终止零,而不仅仅是一个:

    char buff[100] = "this \0 string contains \0 terminators \0";//adds second \0
    printf("%d  != %d\n", strlen(buff), d_strlen(buff));//yields 5 != 37
    

    正如 Frerich Raabe 指出的那样,失去像 strlen 这样经过验证的函数的优化和安全性,这可能是 d_strlen 的更好版本

    size_t d_strlen(const char *in)
    {
        size_t len = 0;
        while(strlen(in+len)) len += strlen(in+len) + 1;
        return len ? --len : 0;//check for zero-length
    }
    

    但是,这会调用 strlen 两次,这是毫无意义的开销,所以你最好写这个:

    size_t d_strlen(const char *in)
    {
        size_t i, len = 0;
        do
        {
            i = strlen(in+len);//get substring length
            len += i + 1;//add to total length + 1 for \0 char
        }while(i > 0);
        return len > 1 ? len-2 : 0;//subtract 2, if possible, else return 0
    }
    

    【讨论】:

    • 鉴于strlen 通常是一个高度优化的函数,重用它可能会更有效(即对每个字符串重复调用它并总结结果)而不是自己迭代,字节-按字节。
    • @FrerichRaabe:公平点,添加了另一个使用strlend_strlen函数实现
    • 酷,我 +1,除了你为每个子字符串调用 strlen(in+len) 两次。 :-}
    • 看起来更好!现在,如果您可以将int 更改为size_t(因为这是strlen 返回的内容)... :-)
    • 抱歉这么迂腐。 intsize_t 确实 对 64 位构建有所不同(在这种情况下,size_t 是 64 位,但 int 可能是 32 位,因此 int i = strlen(..) 会给出截断警告)。
    【解决方案3】:

    C 字符串是由 NUL 终止的字符序列...根据定义,它不能包含多个 NUL。你所拥有的是更通用的东西,一个字符块。为了处理任意的字符块,它们必须伴随着一个长度——字符数的计数。鉴于您可以处理该块而不用担心它是否包含 NUL。给定一个由指向某些字符的指针和计数组成的块,您将使用计数,而不是 strlen,所以

    memmem(buff, buff_len, needle, needle_len);
    

    【讨论】:

    • @EliasVanOotegem 我用 C 语言编写了 35 年,并且是 C 标准委员会的成员,我不同意你的认识论和迂腐,但我不会与你进行详细的辩论。
    猜你喜欢
    • 2012-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多