实现strnstr

时间:2014-06-02 17:07:07

标签: c string strstr

我正在尝试将一个strnstr函数实现为C(strstr,但它检查长度),由于某种原因它不起作用(输出始终为no):

#include <stdio.h>

char *searchingFor = "stackdummy";
char *in = "la da\ndoo a da\nnow here comes the stack\nok there it was.\n";

char *strnstr(char *s1, char *s2, int length) {
    if(s1 == NULL || s2 == NULL) return NULL;
    printf("searching \n\n\"%s\"\n for %.*s\n", s1, length, s2);
    char *ss1 = malloc(strlen(s1) + 1);
    strcpy(ss1, s1);
    char *ss2 = malloc(length + 1);
    strncpy(ss2, s2, length);
    char *result = strstr(ss1, ss2);
    free(ss1);
    free(ss2);
    return result;
}

int main(void) {
    printf("found: %s\n", strnstr(in, searchingFor, 5) ? "yes" : "no");
    printf("found: %s\n", strnstr(in, searchingFor, 5) ? "yes" : "no");
    printf("found: %s\n", strnstr(in, searchingFor, 5) ? "yes" : "no");
    return 0;
}

3 个答案:

答案 0 :(得分:9)

Chris Dodd提供的实施具有以下缺点:

  1. 它违背了strnstr的目的,因为while条件使用无界字符串函数strchr
  2. 这取决于haystack是否为NULL终止,这与strnstr的常规实现有所偏差,例如由GNU-Darwin提供
  3. strchr未内联时,对strchar的调用是不必要的函数调用
  4. haystack为零时,返回NULL而不是len,与已接受的strstr语义的偏差
  5. haystack的长度为零
  6. 时,返回空字符串而不是needle

    以下实现解决了上述问题,而不像GNU-Darwin实现那样难以阅读,并且是Creative Commons许可的:

    #include <string.h>
    
    char *strnstr(const char *haystack, const char *needle, size_t len)
    {
            int i;
            size_t needle_len;
    
            if (0 == (needle_len = strnlen(needle, len)))
                    return (char *)haystack;
    
            for (i=0; i<=(int)(len-needle_len); i++)
            {
                    if ((haystack[0] == needle[0]) &&
                            (0 == strncmp(haystack, needle, needle_len)))
                            return (char *)haystack;
    
                    haystack++;
            }
            return NULL;
    }
    

答案 1 :(得分:3)

怎么样:

char *strnstr(char *haystack, char *needle, size_t len) {
    if (len == 0) return haystack; /* degenerate edge case */
    while (haystack = strchr(haystack, needle[0])) {
        if (!strncmp(haystack, needle, len)) return haystack;
        haystack++; }
    return 0;
}

如果您希望haystack不被终止,则需要两个长度args:

char *memmem(char *haystack, size_t hlen, char *needle, size_t nlen) {
    if (nlen == 0) return haystack; /* degenerate edge case */
    if (hlen < nlen) return 0; /* another degenerate edge case */
    char *hlimit = haystack + hlen - nlen + 1;
    while (haystack = memchr(haystack, needle[0], hlimit-haystack)) {
        if (!memcmp(haystack, needle, nlen)) return haystack;
        haystack++; }
    return 0;
}

在GNU libc中可用,但旧版本已损坏。

答案 2 :(得分:0)

strnstr函数未在C标准中定义,它在BSD和某些其他系统上作为扩展提供。

这是OS / X上的手册页:

NAME

strstrstrcasestrstrnstr-在字符串中找到子字符串

标准C库(libc,-lc)

简介

    #include <string.h>

[...]

    char *strnstr(const char *haystack, const char *needle, size_t len);

[...]

说明

[...]

strnstr()函数在字符串needle中查找以空值终止的字符串haystack的第一个匹配项,在此不多 超过len个字符。不会搜索'\0'字符之后出现的字符。由于strnstr()函数 是FreeBSD专用的API,仅在不考虑可移植性时才应使用。

返回值

如果needle为空字符串,则返回haystack;如果needlehaystack中无处发生,则返回NULL;否则一个指针 返回第一个needle的第一个字符。

示例

以下内容将指针ptr设置为"Bar Baz"的{​​{1}}部分:

largestring

以下内容将指针 const char *largestring = "Foo Bar Baz"; const char *smallstring = "Bar"; char *ptr; ptr = strstr(largestring, smallstring); 设置为ptr,因为只搜索了NULL的前4个字符:

largestring

该规范不够简洁,(Linux内核版本的man page更加不精确),但是BSD系统上的示例(尤其是上面的示例)很清楚: const char *largestring = "Foo Bar Baz"; const char *smallstring = "Bar"; char *ptr; ptr = strnstr(largestring, smallstring, 4); 是最大的在len中要考虑的字节数,而不是haystack中要考虑的字节数,后者只是一个常规的以null结尾的C字符串。

您的功能无法正常运行有多种原因:

  • 您认为needle限制length而不是s2时,语义是不正确的
  • 在您的方法中,复制s1是无用的,并且适得其反:s1(如果非result,将指向分配的副本,该副本在从函数返回之前已释放),因此访问返回值所指向的字符串将具有未定义的行为。
  • 如果源字符串在其自己的空终止符之前至少包含NULL个字符,则
  • strncpy不会终止目标数组。您必须为自己的工作方式设置length,但是真实的ss2[length] = '\0';函数的工作方式也有所不同。
  • 使用strnstr()malloc()可能不是您期望的,并且无法测试潜在的分配失败是一个错误。

这是更正的版本:

free()