1

另请参阅C 分词器


这是我为 C 编写的一个快速 substr() (是的,变量初始化需要移动到函数的开头等,但你明白了)

我已经看到了许多 substr() 的“智能”实现,它们很简单,只需要一行调用 strncpy()!

它们都是错误的(strncpy 不保证空终止,因此调用可能不会产生正确的子字符串!)

这里有更好的东西吗?

把虫子带出来!

char* substr(const char* text, int nStartingPos, int nRun)
{
    char* emptyString = strdup(""); /* C'mon! This cannot fail */

    if(text == NULL) return emptyString;

    int textLen = strlen(text);

    --nStartingPos;

    if((nStartingPos < 0) || (nRun <= 0) || (textLen == 0) || (textLen < nStartingPos)) return emptyString;

    char* returnString = (char *)calloc((1 + nRun), sizeof(char));

    if(returnString == NULL) return emptyString;

    strncat(returnString, (nStartingPos + text), nRun);

    /* We do not need emptyString anymore from this point onwards */

    free(emptyString);
    emptyString = NULL;

    return returnString;
}


int main()
{
    const char *text = "-2--4--6-7-8-9-10-11-";

    char *p = substr(text, -1, 2);
    printf("[*]'%s' (\")\n",  ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 1, 2);
    printf("[*]'%s' (-2)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 3, 2);
    printf("[*]'%s' (--)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 16, 2);
    printf("[*]'%s' (10)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 16, 20);
    printf("[*]'%s' (10-11-)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 100, 2);
    printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 1, 0);
    printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    return 0;
}

输出 :

[*]'' (")
[*]'-2' (-2)
[*]'--' (--)
[*]'10' (10)
[*]'10-11-' (10-11-)
[*]'' (")
[*]'' (")
4

5 回答 5

7

对于应该是一个简单的操作,您的功能似乎非常复杂。一些问题是(并非所有这些都是错误):

  • strdup()和其他内存分配功能可能会失败,您应该考虑到所有可能的问题。
  • 仅在需要时才分配资源(在这种情况下为内存)。
  • 您应该能够区分错误和有效的刺痛。目前,您不知道malloc()失败substr ("xxx",1,1)或工作是否substr ("xxx",1,0)会产生空字符串。
  • 你不需要calloc()记住你无论如何都要覆盖。
  • 所有无效参数都应该导致错误或被强制转换为有效参数(并且您的 API 应该记录哪些参数)。
  • 释放后不需要将本地 emptyString 设置为 NULL - 它会在函数返回时丢失。
  • 您不需要 usr strncat()- 在进行任何复制之前,您应该知道可用的大小和内存,以便您可以(最有可能)更快地使用memcpy().
  • 您使用 base-1 而不是 base-0 来进行字符串偏移违背了 C 的本质。

以下部分是我要做的(我更喜欢负值的 Python 习惯用法从字符串的末尾开始计数,但我保留了长度而不是结束位置)。

char *substr (const char *inpStr, int startPos, int strLen) {
    /* Cannot do anything with NULL. */

    if (inpStr == NULL) return NULL;

    /* All negative positions to go from end, and cannot
       start before start of string, force to start. */

    if (startPos < 0)
        startPos = strlen (inpStr) + startPos;
    if (startPos < 0)
        startPos = 0;

    /* Force negative lengths to zero and cannot
       start after end of string, force to end. */

    if (strLen < 0)
        strLen = 0;
    if (startPos >strlen (inpStr))
        startPos = strlen (inpStr);

    /* Adjust length if source string too short. */

    if (strLen > strlen (&inpStr[startPos]))
        strLen = strlen (&inpStr[startPos]);

    /* Get long enough string from heap, return NULL if no go. */

    if ((buff = malloc (strLen + 1)) == NULL)
        return NULL;

    /* Transfer string section and return it. */

    memcpy (buff, &(inpStr[startPos]), strLen);
    buff[strLen] = '\0';

    return buff;
}
于 2009-05-17T10:33:52.740 回答
5

NULL如果输入无效而不是malloc()空字符串,我会说返回。这样,您可以使用if(p)而不是测试函数是否失败if(*p == 0)

另外,我认为您的函数会泄漏内存,因为emptyStringfree()d 在一个条件中。您应该free()无条件地确保它,即在return.

至于您对strncpy()非 NUL 终止字符串的评论(这是真的),如果您使用calloc()分配字符串而不是malloc(),如果您分配的字节比复制的多一个字节,这不会成为问题,因为calloc()自动设置所有值(包括,在这种情况下,结束)到 0。

我会给你更多的注释,但我讨厌阅读 camelCase 代码。并不是说它有什么问题。

编辑:关于您的更新:

请注意,sizeof(char)无论您的系统如何,C 标准都定义为 1。如果您使用的计算机在一个字节中使用 9 位(上帝保佑),sizeof(char)它仍然是 1。这并不是说有什么问题sizeof(char)- 它清楚地表明了您的意图并提供了对其他类型的调用calloc()malloc()对其他类型的调用的对称性。但sizeof(int)实际上很有用(ints 在 16 和 32 以及这些新奇的 64 位计算机上可以是不同的大小)。你懂得越多。

我还想重申,与大多数其他 C 代码的一致性是返回NULL错误而不是"". 我知道如果您将许多函数(如strcmp()NULL)传递给它们,它们可能会做坏事 NULL - 这是可以预料的。但是 C 标准库(和许多其他 C API)采取的方法是“检查 是调用者的责任NULL,而不是函数的责任,如果他/她不这样做的话。” 如果你想以另一种方式来做,那很酷,但它违背了 C 接口设计中更强大的趋势之一。

另外,我会使用strncpy()(or memcpy()) 而不是strncat(). 使用strncat()(and strcat()) 会掩盖您的意图 -calloc()当您想要做的是设置细绳。strncat()使它看起来像您正在添加到一个字符串,而strcpy()(或另一个复制例程)会使它看起来更像您的意图。以下三行在这种情况下都做同样的事情——选择你认为最好的那一行:

strncat(returnString, text + nStartingPos, nRun);

strncpy(returnString, text + nStartingPos, nRun);

memcpy(returnString, text + nStartingPos, nRun);

另外,strncpy()并且memcpy()可能会比strncat().

text + nStartingPos是一样的nStartingPos + text- 我会放第char *一个,因为我认为这更清楚,但无论你想把它们放在什么顺序都取决于你。此外,它们周围的括号是不必要的(但很好),因为+优先级高于,.

编辑2:三行代码不做同样的事情,但在这种情况下,它们都会产生相同的结果。谢谢你抓住我。

于 2009-05-17T06:42:56.943 回答
1
char* emptyString = strdup(""); /* C'mon! This cannot fail? */

您需要检查是否为空。请记住,它仍然必须为空字符分配 1 个字节。

于 2009-05-17T06:35:26.197 回答
0

strdup 可能会失败(尽管它不太可能并且不值得检查,恕我直言)。然而,它确实有另一个问题——它不是标准 C 函数。最好使用malloc。

于 2009-05-17T06:48:48.740 回答
0

您还可以使用 memmove 函数返回从 start 到 length 的子字符串。从 paxdiablo 的解决方案改进/添加另一个解决方案:

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

    char *splitstr(char *idata, int start, int slen) {
            char ret[150];
            if(slen == NULL) {
                    slen=strlen(idata)-start;
            }
            memmove (ret,idata+start,slen);
            return ret;
    }

    /*
    Usage:
            char ostr[]="Hello World!";
            char *ores=splitstr(ostr, 0, 5);
            Outputs:
                    Hello
    */

希望能帮助到你。使用 TCC C 编译器在 Windows 7 Home Premium 上测试。

于 2012-08-01T20:55:52.840 回答