【问题标题】:Why is substring not part of the C standard library?为什么 substring 不是 C 标准库的一部分?
【发布时间】:2011-11-16 09:35:25
【问题描述】:

我知道 C 是有目的的准系统,但我很好奇为什么 中不包含像子字符串函数这样司空见惯的东西。

难道没有一种“足够正确”的方法可以做到这一点吗?太多特定领域的要求?任何人都可以解释一下吗?

顺便说一句,这是我经过一番研究后提出的子字符串函数。 编辑:我根据 cmets 做了一些更新。

void substr (char *outStr, const char *inpStr, int startPos, size_t strLen) {
    /* Cannot do anything with NULL. */
    if (inpStr == NULL || outStr == NULL) return;

    size_t len = strlen (inpStr);

    /* All negative positions to go from end, and cannot
    start before start of string, force to start. */
    if (startPos < 0) {
        startPos = len + startPos;
    }
    if (startPos < 0) {
        startPos = 0;
    }

    /* Force negative lengths to zero and cannot
    start after end of string, force to end. */
    if ((size_t)startPos > len) {
        startPos = len;
    }

    len = strlen (&inpStr[startPos]);
    /* Adjust length if source string too short. */
    if (strLen > len) {
        strLen = len;
    }

    /* Copy string section */
    memcpy(outStr, inpStr+startPos, strLen);
    outStr[strLen] = '\0';
}

编辑:根据 r 的评论,我也想出了这个班轮。不过,你自己来检查!

#define substr(dest, src, startPos, strLen) snprintf(dest, BUFF_SIZE, "%.*s", strLen, src+startPos)

【问题讨论】:

  • 不会strncpy 让你做同样的事情吗?
  • 任何询问“为什么 X 标准不包括功能 Y”的问题都很难明确回答。
  • strncpy 并不像您认为的那样。我个人在这里使用memcpy。 (另外,size_t 类型更适合数组索引和大小。)
  • 如果您向 10 个 C 程序员询问通用子字符串函数的规范,您可能会得到 10 个不同的答案。它应该分配内存吗?它应该允许负索引吗?我们是否需要一个 substringn 函数来获取目标缓冲区的长度?等等
  • @Tom:strncpy不太可能是任何特定问题的正确答案。

标签: c substring substr


【解决方案1】:

基本的标准库函数不会为自己负担过多昂贵的安全检查,而将它们留给用户。您在实现中执行的大多数安全检查都是昂贵的:在这样一个基本的库函数中完全不可接受。这是 C,不是 Java。

一旦你从图片中得到一些检查,“substrung”函数归结为普通的strlcpy。即忽略startPos上的安全检查,你需要做的就是

char *substr(const char *inpStr, char *outStr, size_t startPos, size_t strLen) {
  strlcpy(outStr, inpStr + startPos, strLen);
  return outStr;
}

虽然strlcpy 不是标准库的一部分,但它可以被[误用] strncpy 粗略地替换。同样,忽略startPos 上的安全检查,您需要做的就是

char *substr(const char *inpStr, char *outStr, size_t startPos, size_t strLen) {
  strncpy(outStr, inpStr + startPos, strLen);
  outStr[strLen] = '\0';
  return outStr;
}

具有讽刺意味的是,在您的代码中 strncpy 以同样的方式被滥用。最重要的是,您的许多安全检查都是您选择有符号类型 (int) 来表示索引的直接结果,而正确的类型应该是无符号类型 (size_t)。

【讨论】:

  • 正确的使用方法是什么?
  • 我宁愿在这里看到memcpy 而不是strncpy
  • @Chris Lutz:memcpy 不会在输入中终止 \0。要使用memcpy,您必须先计算strlen。我同意 strncpy 在这里完全被滥用,但我的目标是简洁。
  • @Derek Springer:strncpy 是一个将 C 字符串转换为固定宽度字符串的函数。这就是它的用途。将其用于“安全”字符串复制是对编程的犯罪。 stackoverflow.com/questions/2114896/…
  • 我喜欢 void 函数返回 char * 的方式。 (我实际上认为,如果它出于初始化目的返回它会更好)
【解决方案2】:

也许是因为它是单行的:

snprintf(dest, dest_size, "%.*s", sub_len, src+sub_start);

【讨论】:

  • 你知道 snprintf 相对于 memcpy 的效率吗?
  • @Derek Springer:在字符串处理中,您应该更担心安全性而不是效率。如果您在经过漫长而清醒的分析之后,确定在您的应用程序中使用 snprintf 太慢,那么您最好避免使用直接子字符串操作,而不是使用 memcpy 而不是 snprintf
  • snprintf 是否保证目标缓冲区的空终止?
  • @Selbie:是的——我想知道同样的事情。从手册页:函数 snprintf() 和 vsnprintf() 最多将 size 个字节(包括尾随空字节 ('\0'))写入 str。
  • 是的snprintf 总是终止,除非dest_size 为零,在这种情况下它不会写任何东西。
【解决方案3】:

您确实有 strcpystrncpy。你还不够吗?使用strcpy,您可以模拟从字符到结尾的子字符串,使用strncpy,您可以从字符模拟多个字符的子字符串(您只需要记住在字符串末尾添加\0)。 strncpy 甚至比 C# 等效项更好,因为您可以超出子字符串的长度并且不会引发错误(如果您在 dest 中分配了足够的空间,即使 src 为 long 1,您也可以执行 strncpy(dest, src, 1000) . 在 C# 中你不能。) 正如评论中所写,您甚至可以使用memcpy,但请记住始终在字符串末尾添加\0,并且您必须知道要复制多少个字符(因此您必须知道确切 src 子字符串的长度)并且如果您想重构代码以使用 wchar_t 并且它不是类型安全的(因为它接受 void* 而不是 char*),使用起来会稍微复杂一些。所有这些都是为了换取比strncpy更快一点的速度

【讨论】:

  • strncpy 不是“安全的strcpy”。小心使用它的代码。
  • @Chris 如果你总是用“奖金\0”终止它,我认为使用它没有任何问题。但是,是的,你没有传递给它 bufferlength-1 的事实是一个大问题 :-) 但最后......对我来说更多的钱:-) :-)
  • 我更喜欢尽可能使用memcpy。它不会执行检查空终止或用零填充未使用空间的额外(通常是不必要的)工作,并且您始终知道它复制了多少数据。
  • @xanatos:但是不必要的填充呢? strncpy 是为一个非常特殊的目的而设计的,我们现在很少遇到这个目的。恕我直言,它不应该在标准库中。
  • @Chris,我认为您应该将“避免浮点运算”替换为“比浮点运算更快”。如果避免使用浮点的原因是其他原因(比如想要可重现的位精确答案),那么这可能是一个非常合理的问题,而不是过早的优化。
【解决方案4】:

在 C 语言中,您有一个函数通过指针从字符串中返回符号子集:strstr

char *ptr;
char string1[] = "Hello World";
char string2[] = "World";

ptr = strstr(string1, string2)

*ptr 将指向第一个出现的字符。

顺便说一句,你写的不是函数而是过程,ANSI字符串函数:string.h

【讨论】:

  • 这与 selbie 最初的评论相同——我认为这并不完全相同:strstr 返回一个指向 str1 中第一次出现 str2 的指针——只有在你确切知道什么时才有帮助您正在寻找。
【解决方案5】:

这是您想要的更轻量级的版本。避免多余的 strlen 调用并保证目标缓冲区上的 null 终止(strncpy 不会这样做)。

void substr(char* pszSrc, int start, int N, char* pszDst, int lenDest)
{
    const char* psz = pszSrc + start;
    int x = 0;

    while ((x < N) && (x < lenDest))
    {
        char ch = psz[x];
        pszDst[x] = ch;
        x++;
        if (ch == '\0')
        {
           return;
        }
    }

    // guarantee null termination
    if (x > 0)
    {    
        pszDest[x-1] = 0;
    }
}

Example:
char *pszLongString = "This is a long string";
char szSub[10];
substr(pszLongString, 0, 4, szSub, 10); // copies "long" into szSub and includes the null char

因此,虽然 C 中没有正式的子字符串函数,但 C++ 字符串类通常有这样的方法:

#include <string>
...
std::string str;
std::string strSub;

str = "This is a long string";

strSub = str.substr(10, 4); // "long"

printf("%s\n", strSub.c_str());

【讨论】:

  • 我认为这并不完全相同:strstr 返回一个指向 str1 中第一次出现的 str2 的指针——只有在您确切知道要查找的内容时才有用。我所说的只是从“汉堡包”返回“burg”。
  • 你的新函数有 5 个参数,但你只用 4 个参数调用它。
  • @Oli - 因为我给出了“C”和“C++”的答案,所以你投反对票是相当严厉的。
  • @selbie - 在他投反对票时,您只有 C++ 答案。
【解决方案6】:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char* substr(const char *string, size_t from, size_t to);

int main(int argc, char *argv[])
{
    char *string = argv[1];

    const char *substring = substr(string,6,80);

    printf("string is [%s] substring is [%s]\n",string,substring);

    return 0;
}

const char* substr(const char *string, size_t from, size_t to)
{
    if (to <= from) 
        return NULL;

    if (from >= to)
        return NULL;

    if (string == NULL)
        return NULL;

    if (strlen(string) == 0)
        return NULL;

    if (from < 0)
        from = 0;

    if (to > strlen(string))
        to = strlen(string);

    char *substring = malloc(sizeof(char) * ((to-from)+1));

    size_t index;

    for (index = 0; from < to; from++, index++)
        substring[index] = string[from];

    substring[index] = '\0';

    return substring;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-17
    相关资源
    最近更新 更多