strstr 是不是有反向功能

Posted

技术标签:

【中文标题】strstr 是不是有反向功能【英文标题】:Is there a reverse function for strstrstrstr 是否有反向功能 【发布时间】:2009-10-27 23:43:17 【问题描述】:

我正在尝试找到与strstr 类似的函数,它从字符串的末尾开始搜索子字符串。

【问题讨论】:

嗯我不确定我是否应该修改这个问题。但我还有一个。是否有 C 库函数来查找字符串中子字符串最后一次出现的索引? 使用 strrstr() 和指针算法也可以得到同样的效果。 @ManavSharma 为什么您要在评论中提出另一个问题或考虑修改现有问题?这个网站是关于问题的,你可以问多少你想问多少。另一个问题意味着您在此平台上创建了另一个问题。 【参考方案1】:

标准 C 库没有“反向 strstr”函数,因此您必须自己查找或编写。

我想出了几个我自己的解决方案,并在这个线程中添加了一些测试和基准测试代码以及​​其他功能。对于那些好奇,在我的笔记本电脑(Ubuntu karmic,amd64 架构)上运行的输出如下所示:

$ gcc -O2 --std=c99 strrstr.c && ./a.out
#1 0.123 us last_strstr
#2 0.440 us theo
#3 0.460 us cordelia
#4 1.690 us digitalross
#5 7.700 us backwards_memcmp
#6 8.600 us sinan

您的结果可能不同,并且根据您的编译器和库,结果的顺序也可能不同。

要从字符串的开头获取匹配的偏移量(索引),使用指针算法:

char *match = last_strstr(haystack, needle);
ptrdiff_t index;
if (match != NULL)
    index = match - haystack;
else
    index = -1;

现在,落叶松(请注意,这纯粹是用 C 语言编写的,我对 C++ 的了解还不够,无法给出答案):

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

/* By liw. */
static char *last_strstr(const char *haystack, const char *needle)

    if (*needle == '\0')
        return (char *) haystack;

    char *result = NULL;
    for (;;) 
        char *p = strstr(haystack, needle);
        if (p == NULL)
            break;
        result = p;
        haystack = p + 1;
    

    return result;



/* By liw. */
static char *backwards_memcmp(const char *haystack, const char *needle)

    size_t haylen = strlen(haystack);

    if (*needle == '\0')
        return (char *) haystack;

    size_t needlelen = strlen(needle);
    if (needlelen > haylen)
        return NULL;

    const char *p = haystack + haylen - needlelen;
    for (;;) 
        if (memcmp(p, needle, needlelen) == 0)
            return (char *) p;
        if (p == haystack)
            return NULL;
        --p;
    



/* From http://stuff.mit.edu/afs/sipb/user/cordelia/Diplomacy/mapit/strrstr.c
 */
static char *cordelia(const char *s1, const char *s2)

 const char *sc1, *sc2, *psc1, *ps1;

 if (*s2 == '\0')
  return((char *)s1);

 ps1 = s1 + strlen(s1);

 while(ps1 != s1) 
  --ps1;
  for (psc1 = ps1, sc2 = s2; ; )
   if (*(psc1++) != *(sc2++))
    break;
   else if (*sc2 == '\0')
    return ((char *)ps1);
 
 return ((char *)NULL);



/* From http://***.com/questions/1634359/
   is-there-a-reverse-fn-for-strstr/1634398#1634398 (DigitalRoss). */
static char *reverse(const char *s)

  if (s == NULL)
    return NULL;
  size_t i, len = strlen(s);
  char *r = malloc(len + 1);

  for(i = 0; i < len; ++i)
    r[i] = s[len - i - 1];
  r[len] = 0;
  return r;

char *digitalross(const char *s1, const char *s2)

  size_t  s1len = strlen(s1);
  size_t  s2len = strlen(s2);
  const char *s;

  if (s2len == 0)
    return (char *) s1;
  if (s2len > s1len)
    return NULL;
  for (s = s1 + s1len - s2len; s >= s1; --s)
    if (strncmp(s, s2, s2len) == 0)
      return (char *) s;
  return NULL;



/* From http://***.com/questions/1634359/
  is-there-a-reverse-fn-for-strstr/1634487#1634487 (Sinan Ünür). */

char *sinan(const char *source, const char *target)

    const char *current;
    const char *found = NULL;

    if (*target == '\0')
        return (char *) source;

    size_t target_length = strlen(target);
    current = source + strlen(source) - target_length;

    while ( current >= source ) 
        if ( (found = strstr(current, target)) ) 
            break;
        
        current -= 1;
    

    return (char *) found;



/* From http://***.com/questions/1634359/
  is-there-a-reverse-fn-for-strstr/1634441#1634441 (Theo Spears). */
char *theo(const char* haystack, const char* needle)

  size_t needle_length = strlen(needle);
  const char* haystack_end = haystack + strlen(haystack) - needle_length;
  const char* p;
  size_t i;

  if (*needle == '\0')
    return (char *) haystack;
  for(p = haystack_end; p >= haystack; --p)
  
    for(i = 0; i < needle_length; ++i) 
      if(p[i] != needle[i])
        goto next;
    
    return (char *) p;

    next:;
  
  return 0;



/*
 * The rest of this code is a test and timing harness for the various
 * implementations above.
 */


#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>


/* Check that the given function works. */
static bool works(const char *name, char *(*func)(const char *, const char *))

    struct 
        const char *haystack;
        const char *needle;
        int offset;
     tests[] = 
         "", "", 0 ,
         "", "x", -1 ,
         "x", "", 0 ,
         "x", "x", 0 ,
         "xy", "x", 0 ,
         "xy", "y", 1 ,
         "xyx", "x", 2 ,
         "xyx", "y", 1 ,
         "xyx", "z", -1 ,
         "xyx", "", 0 ,
    ;
    const int num_tests = sizeof(tests) / sizeof(tests[0]);
    bool ok = true;

    for (int i = 0; i < num_tests; ++i) 
        int offset;
        char *p = func(tests[i].haystack, tests[i].needle);
        if (p == NULL)
            offset = -1;
        else
            offset = p - tests[i].haystack;
        if (offset != tests[i].offset) 
            fprintf(stderr, "FAIL %s, test %d: returned %d, haystack = '%s', "
                            "needle = '%s', correct return %d\n",
                            name, i, offset, tests[i].haystack, tests[i].needle,
                            tests[i].offset);
            ok = false;
        
    
    return ok;



/* Dummy function for calibrating the measurement loop. */
static char *dummy(const char *haystack, const char *needle)

    return NULL;



/* Measure how long it will take to call the given function with the
   given arguments the given number of times. Return clock ticks. */
static clock_t repeat(char *(*func)(const char *, const char *),
                       const char *haystack, const char *needle,
                       long num_times)

    clock_t start, end;

    start = clock();
    for (long i = 0; i < num_times; ++i) 
        func(haystack, needle);
    
    end = clock();
    return end - start;



static clock_t min(clock_t a, clock_t b)

    if (a < b)
        return a;
    else
        return b;



/* Measure the time to execute one call of a function, and return the
   number of CPU clock ticks (see clock(3)). */
static double timeit(char *(*func)(const char *, const char *))

    /* The arguments for the functions to be measured. We deliberately
       choose a case where the haystack is large and the needle is in
       the middle, rather than at either end. Obviously, any test data
       will favor some implementations over others. This is the weakest
       part of the benchmark. */

    const char haystack[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "b"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    const char needle[] = "b";

    /* First we find out how many repeats we need to do to get a sufficiently
       long measurement time. These functions are so fast that measuring
       only a small number of repeats will give wrong results. However,
       we don't want to do a ridiculously long measurement, either, so 
       start with one repeat and multiply it by 10 until the total time is
       about 0.2 seconds. 

       Finally, we measure the dummy function the same number of times
       to get rid of the call overhead.

       */

    clock_t mintime = 0.2 * CLOCKS_PER_SEC;
    clock_t clocks;
    long repeats = 1;
    for (;;) 
        clocks = repeat(func, haystack, needle, repeats);
        if (clocks >= mintime)
            break;
        repeats *= 10;
    

    clocks = min(clocks, repeat(func, haystack, needle, repeats));
    clocks = min(clocks, repeat(func, haystack, needle, repeats));

    clock_t dummy_clocks;

    dummy_clocks = repeat(dummy, haystack, needle, repeats);
    dummy_clocks = min(dummy_clocks, repeat(dummy, haystack, needle, repeats));
    dummy_clocks = min(dummy_clocks, repeat(dummy, haystack, needle, repeats));

    return (double) (clocks - dummy_clocks) / repeats / CLOCKS_PER_SEC;



/* Array of all functions. */
struct func 
    const char *name;
    char *(*func)(const char *, const char *);
    double secs;
 funcs[] = 
#define X(func)  #func, func, 0 
    X(last_strstr),
    X(backwards_memcmp),
    X(cordelia),
    X(digitalross),
    X(sinan),
    X(theo),
#undef X
;
const int num_funcs = sizeof(funcs) / sizeof(funcs[0]);


/* Comparison function for qsort, comparing timings. */
int funcmp(const void *a, const void *b)

    const struct func *aa = a;
    const struct func *bb = b;

    if (aa->secs < bb->secs)
        return -1;
    else if (aa->secs > bb->secs)
        return 1;
    else
        return 0;



int main(void)


    bool ok = true;
    for (int i = 0; i < num_funcs; ++i) 
        if (!works(funcs[i].name, funcs[i].func)) 
            fprintf(stderr, "%s does not work\n", funcs[i].name);            
            ok = false;
        
    
    if (!ok)
        return EXIT_FAILURE;

    for (int i = 0; i < num_funcs; ++i)
        funcs[i].secs = timeit(funcs[i].func);
    qsort(funcs, num_funcs, sizeof(funcs[0]), funcmp);
    for (int i = 0; i < num_funcs; ++i)
        printf("#%d %.3f us %s\n", i+1, funcs[i].secs * 1e6, funcs[i].name);

    return 0;

【讨论】:

对不起,长度。所有有趣的部分(反向 strstr 的实际实现)都在代码的顶部,所以应该很容易找到。 你应该改进你的测试。干草堆和针太短,用大弦试试。 如果needle是空字符串,last_strstr不应该返回haystack的结尾而不是开头吗? 我更喜欢while (1)而不是for (;;),它更好【参考方案2】:

我不知道有一个。 C 语言的优点之一是,如果您编写自己的函数,它与库函数一样快速和高效。 (在许多其他语言中完全不是这种情况。)

你可以反转字符串和子字符串,然后搜索。

最后,当字符串库不够好时,人们经常做的另一件事是转向正则表达式。

好的,我写了reverse()rstrstr(),如果我们幸运的话,这可能会起作用。摆脱 C++ 的 __restrict。您可能还希望将参数设为const,但随后您将需要转换返回值。要回答您的评论问题,您只需从中减去原始字符串指针即可从子字符串的地址中获取索引。好的:

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

char *reverse(const char * __restrict const s)

  if (s == NULL)
    return NULL;
  size_t i, len = strlen(s);
  char *r = malloc(len + 1);

  for(i = 0; i < len; ++i)
    r[i] = s[len - i - 1];
  r[len] = 0;
  return r;


char *rstrstr(char *__restrict s1, char *__restrict s2)

  size_t  s1len = strlen(s1);
  size_t  s2len = strlen(s2);
  char *s;

  if (s2len > s1len)
    return NULL;
  for (s = s1 + s1len - s2len; s >= s1; --s)
    if (strncmp(s, s2, s2len) == 0)
      return s;
  return NULL;

【讨论】:

显然我的陈述暗示“除非你搞砸了实现”,但我们不能在不包括这个假设的情况下对任何事情做出任何陈述。我的意思是,认真现在。 你们忽略了整体要点,即您的例程是用 C 编写的,库例程是用 C 编写的,每个人都处于公平的竞争环境中。让我们看看有人在 Perl、Python、Ruby 甚至 Java 中实现 strcmp。这就是,咳咳,明显,要点。 库例程不一定是用 C 编写的。当我在编写标准库时,我通常会编写程序集。您几乎可以用任何了解目标平台上的 C 调用约定的编译语言编写 C 库函数。 (也就是说,我同意这一切都是题外话)。 标准库几乎可以肯定不使用 C;它要么使用程序集,要么使用带有编译器特定内在函数的 C。基本上不可能让 strrstr 和标准库 strstr 一样快。 @Alice GlibC strstr 几乎是普通的 C。如果haystack 超过一定长度,它甚至会使用一个跳过表的巧妙搜索策略的速度结果。在今天大多数时候使用汇编几乎没有回报,因为编译器从 C 生成的汇编代码比 10 个开发人员中的 9 个可以手动生成更好的汇编代码,并且内在函数仅在 CPU 可以执行某些无法用 C 表示的情况下才有意义(例如计算int 中的 1 位的数量,几乎任何 CPU 都可以做到,C 不能;或旋转 int 的位) - 但这些都无助于实现strstr【参考方案3】:

如果你会使用 C++,你可以像这样搜索字符串:

std::string::iterator found=std::search(haystack.rbegin(), haystack.rend(), needle.rbegin(), needle.rend()).base();
// => yields haystack.begin() if not found, otherwise, an iterator past-the end of the occurence of needle

【讨论】:

和 std::w/string::find_last_of 甚至std::string_view::find_last_of【参考方案4】:

一种可能的(如果不是完全优雅的)实现可能如下所示:

#include "string.h"

const char* rstrstr(const char* haystack, const char* needle)

  int needle_length = strlen(needle);
  const char* haystack_end = haystack + strlen(haystack) - needle_length;
  const char* p;
  size_t i;

  for(p = haystack_end; p >= haystack; --p)
  
    for(i = 0; i < needle_length; ++i) 
      if(p[i] != needle[i])
        goto next;
    
    return p;

    next:;
  
  return 0;

【讨论】:

有点吹毛求疵,我会做所有这些const char *。 codepad.org/KzryjtRE @Kinopiko:实际上,这甚至不是吹毛求疵。如果调用者的“needle”或“haystack”已经是 const,则不使用 const 会使调用者难以使用此函数。 int 应该是 size_t。但我几乎想为goto 投票。 你还需要制作haystack_endp以及函数const char *的返回值。请参阅我在 codepad.org 上的粘贴。【参考方案5】:

没有。这是 C++ std::string 类具有明显优势的地方之一——除了std::string::find(),还有std::string::rfind()

【讨论】:

【参考方案6】:

我认为您仍然可以使用库函数来做到这一点。

1.使用strrev函数反转字符串。

2.使用strstr函数做任何你想做的事情。

3.你可以通过从原始字符串的长度中减去搜索字符串的起始索引来找到搜索字符串的起始索引(从反向)。

【讨论】:

打算试试这个。呵呵.. strrev 没有找到.. 看看它可能住在哪里。 strrev 在 linux 或 os/x 上的 string.h 中不存在 ***.com/questions/8534274/… 要小心这样做 - 如果你要这样做,你还需要反转你的针线。【参考方案7】:

尽管是非标准的,strrstr 得到了广泛的支持,并且完全符合您的要求。

【讨论】:

这就是我要找的:) 嗯,这么小功能需要安装一个lib。我希望 libc 拥有它!【参考方案8】:

Here is one. 测试它是我留给你的练习:)

【讨论】:

【参考方案9】:

是否有 C 库函数来查找字符串中最后一次出现的子字符串的索引?

编辑:正如@hhafez 在下面的评论中指出的那样,我为此发布的第一个解决方案效率低下且不正确(因为我将指针推进了target_length,这在我的愚蠢测试中运行良好) .您可以在编辑历史记录中找到该版本。

这是一个从末尾开始并返回的实现:

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

const char *
findlast(const char *source, const char *target) 
    const char *current;
    const char *found = NULL;

    size_t target_length = strlen(target);
    current = source + strlen(source) - target_length;

    while ( current >= source ) 
        if ( (found = strstr(current, target)) ) 
            break;
        
        current -= 1;
    

    return found;


int main(int argc, char *argv[]) 
    if ( argc != 3 ) 
        fputs("invoke with source and search strings as arguments", stderr);
        return EXIT_FAILURE;
    

    const char *found = findlast(argv[1], argv[2]);

    if ( found ) 
        printf("Last occurence of '%s' in '%s' is at offset %d\n",
                argv[2], argv[1], found - argv[1]
                );
    
    return 0;

输出:

C:\Temp> st "这是一个测试这个的测试字符串" test “这是一个测试这个的测试字符串”中最后出现的“测试”是 在偏移 27

【讨论】:

但这与编写自己的例程相比很难看,如果字符串真的很长怎么办?也不要忘记他期望有多次出现,他希望在最后找到它们,这就是为什么他想从头开始而不是从头开始。【参考方案10】:

我不相信 c 字符串库中有,但是编写自己的代码很简单,在一种情况下,您知道字符串的长度或者它被正确终止。

【讨论】:

【参考方案11】:

标准 C 库中没有。您可以在网上找到一个,或者您可能需要自己编写。

【讨论】:

【参考方案12】:

长话短说:

不 - C 库中没有功能可以满足您的需求..

但正如其他人指出的那样:编写这样的函数不是火箭科学......

【讨论】:

写一个慢的实现不是火箭科学,但是让它快需要一些花哨的算法。【参考方案13】:

感谢您的回答!还有另一种方法来自 MSDN 论坛。 http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/ed0f6ef9-8911-4879-accb-b3c778a09d94

【讨论】:

【参考方案14】:
char * strrstr(char *_Str, char *_SubStr)
    char *returnPointer, *p;

    //find 1st occurence. if not found, return NULL
    if ( (p=strstr(_Str, _SubStr))==NULL)
        return NULL;

    //loop around until no more occurences
    do
        returnPointer=p;
        ++p;
    while(p=strstr(p, _SubStr));

    return returnPointer;

【讨论】:

【参考方案15】:

您可以为此目的使用标准算法 std::find_end。例如

    char s[] = "What is the last word last";
    char t[] = "last";

    std::cout << std::find_end( s, s + sizeof( s ) - 1, t, t + sizeof( t ) -1 )
              << std::endl;

【讨论】:

【参考方案16】:

这是我能想到的最简单的植入。与此函数的其他实现不同,它避免了像 user3119703 这样的其他人所进行的初始 strstr 调用。

char * lastStrstr(const char * haystack,const char * needle)
    char*temp=haystack,*before=0;
    while(temp=strstr(temp,needle)) before=temp++;
    return before;

【讨论】:

当按照 OP 的要求搜索子字符串(而不是从末尾开始并从头开始搜索)时,看起来它仍然从“干草堆”的开头开始。【参考方案17】:
char* strrstr(char * _Str, const char * _SubStr)

    const BYTE EQUAL=0;
    int i=0, src_len = strlen(_Str), find_len = strlen(_SubStr),
        tail_count=0;

    for(i=src_len; i>-1; i--)
    
        if(_Str[i] == _SubStr[0] && tail_count >= find_len)
        
            if(strncmp(&_Str[i], _SubStr, find_len) == EQUAL)
            
                return &_Str[i];
            
        
        tail_count++;
    
    return NULL;    

【讨论】:

也解释一下 @TalhaIrfan 用目标字符串的长度,因为在末尾减少索引以找到所需的字符串。 - 谷歌翻译

以上是关于strstr 是不是有反向功能的主要内容,如果未能解决你的问题,请参考以下文章

C语言试题196之实现strstr函数功能

C语言试题196之实现strstr函数功能

C语言试题196之实现strstr函数功能

Keras 是不是具有将输入词向量复制并反向传播到仅一组的功能?

模拟实现strstr

FormData上传功能反向代理服务器(第三期)