这是C的好子系统吗?

时间:2009-05-17 06:32:27

标签: c substring substr

另见C Tokenizer


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

我见过很多简单的衬里调用strncpy()的“聪明”的substr()实现!

它们都是错的(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-)
[*]'' (")
[*]'' (")

5 个答案:

答案 0 :(得分:7)

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

  • strdup(),以及其他内存分配函数, 可能失败,您应该考虑所有可能的问题。
  • 只在需要时分配资源(在这种情况下是内存)。
  • 你应该能够区分错误和有效的叮咬。目前,您不知道malloc()失败substr ("xxx",1,1)或工作substr ("xxx",1,0)是否会产生空字符串。
  • 无论如何你都不需要calloc()内存。
  • 所有无效参数都应该导致错误或被强制转换为有效参数(并且您的API应该记录哪些参数)。
  • 释放后你不需要将本地emptyString设置为NULL - 它会在函数返回时丢失。
  • 你不需要usr strncat() - 在执行任何复制之前,你应该知道你可用的大小和内存,这样你就可以使用(最有可能)更快{{} 1}}。
  • 你使用base-1而不是base-0来实现字符串偏移与C的粒度相反。

以下部分就是我要做的事情(我更喜欢从字符串末尾开始计算负值的Python习语,但我保持长度而不是结束位置。)

memcpy()

答案 1 :(得分:5)

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

另外,我认为你的函数会泄漏内存,因为emptyString在一个条件中只有free() d。您应该无条件地确保free(),即在return之前。

关于你对strncpy()的评论而不是NUL-终止字符串(这是真的),如果你使用calloc()来分配字符串而不是malloc(),那么这不是如果您分配比复制多一个字节的问题,则会出现问题,因为calloc()会自动将所有值(在这种情况下,包括结尾)设置为0。

我会给你更多笔记,但我讨厌阅读camelCase代码。并不是说它有任何问题。

编辑:关于您的更新:

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

我还想重申,与大多数其他C代码的一致性是在错误而不是NULL上返回""。我知道很多函数(比如strcmp())可能会做坏事,如果你把它们传递给NULL - 这是可以预料的。但是C标准库(以及许多其他C API)采用的方法是“调用者负责检查NULL,而不是函数负责给他/她做好准备,如果他没有。”如果你想以另一种方式做到这一点,那很酷,但这与C界面设计中更强烈的趋势相反。

另外,我会使用strncpy()(或memcpy())而不是strncat()。使用strncat()(和strcat())会模糊您的意图 - 这会让某人看到您的代码,认为您想要添加到字符串的末尾(您执行此操作,因为在calloc()之后,结束是开始),当你想要做的是设置字符串。 strncat()使您看起来像是在添加字符串,而strcpy()(或其他复制例程)会使它看起来更像您的意图。在这种情况下,以下三行都做同样的事情 - 选择你认为最好的那一行:

strncat(returnString, text + nStartingPos, nRun);

strncpy(returnString, text + nStartingPos, nRun);

memcpy(returnString, text + nStartingPos, nRun);

另外,strncpy()memcpy()可能比strncat()更快/更有效。

text + nStartingPosnStartingPos + text相同 - 我会将char *放在第一位,因为我认为更清楚,但无论您想要将它们放入哪个顺序取决于您。此外,它们周围的括号是不必要的(但很好),因为+的优先级高于,

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

答案 2 :(得分:1)

char* emptyString = strdup(""); /* C'mon! This cannot fail? */

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

答案 3 :(得分:0)

strdup可能会失败(虽然它不太可能,不值得检查,恕我直言)。但确实有另一个问题 - 它不是标准C功能。使用malloc会更好。

答案 4 :(得分:0)

您还可以使用memmove函数从开始到长度返回子字符串。 改进/添加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 Compilier在Windows 7 Home Premium上测试。