home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume32 / qsearch / part01 < prev    next >
Encoding:
Text File  |  1992-10-17  |  9.2 KB  |  302 lines

  1. Newsgroups: comp.sources.misc
  2. From: gg@trillian.tp1.ruhr-uni-bochum.de (Guido Gronek)
  3. Subject:  v32i106:  qsearch - fast substring matching in either direction, Part01/01
  4. Message-ID: <1992Oct16.143720.10071@sparky.imd.sterling.com>
  5. X-Md4-Signature: cc67e3c306ace73ce096f9d65f2e5861
  6. Date: Fri, 16 Oct 1992 14:37:20 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: gg@trillian.tp1.ruhr-uni-bochum.de (Guido Gronek)
  10. Posting-number: Volume 32, Issue 106
  11. Archive-name: qsearch/part01
  12. Environment: ANSI-C
  13.  
  14. Recently I needed a fast substring search algorithm, scanning the
  15. text string from right to left. The result is an ANSI-C implementation
  16. of a function called qsearch(), searching for the leftmost or rightmost
  17. occurrance of a pattern string in a text string.
  18.  
  19. The algorithm used is 'quick search' by D. M. Sunday, which is a
  20. simple but fast practical method. It's supposed to be even faster
  21. than the well known Boyer-Moore algorithm, see Sunday's original
  22. paper CACM V33 N8, page 132 (for several improvements of the basic
  23. method as well). The reversed text scanning I have realized by a
  24. rather simple variation of the original algorithm.
  25.  
  26. -----
  27. #!/bin/sh
  28. # This is a shell archive (shar 3.21)
  29. # made 10/12/1992 20:25 UTC by gg@trillian
  30. #
  31. # existing files WILL be overwritten
  32. #
  33. # This shar contains:
  34. # length  mode       name
  35. # ------ ---------- ------------------------------------------
  36. #   2749 -rw-r--r-- qsearch.txt
  37. #   3482 -rw-r--r-- qsearch.c
  38. #    316 -rw-r--r-- qsearch.h
  39. #
  40. if touch 2>&1 | fgrep '[-amc]' > /dev/null
  41.  then TOUCH=touch
  42.  else TOUCH=true
  43. fi
  44. # ============= qsearch.txt ==============
  45. echo "x - extracting qsearch.txt (Text)"
  46. sed 's/^X//' << 'SHAR_EOF' > qsearch.txt &&
  47. X*
  48. X* qsearch: fast substring matching in forward and backward direction
  49. X* 9-Oct-1992, Guido Gronek
  50. X*
  51. X
  52. XRecently I needed a fast substring search algorithm, scanning the
  53. Xtext string from right to left. The result is an ANSI-C implementation
  54. Xof a function called qsearch(), searching for the leftmost or rightmost
  55. Xoccurrance of a pattern string in a text string.
  56. X
  57. XThe algorithm used is 'quick search' by D. M. Sunday, which is a
  58. Xsimple but fast practical method. It's supposed to be even faster
  59. Xthan the well known Boyer-Moore algorithm, see Sunday's original
  60. Xpaper CACM V33 N8, page 132 (for several improvements of the basic
  61. Xmethod as well). The reversed text scanning I have realized by a
  62. Xrather simple variation of the original algorithm.
  63. X
  64. XThe fastness of 'quick search' bases on the generation of a shift table
  65. X(called 'table delta 1' by Sunday) from the pattern string first. This
  66. Xpermits to perform the actual searching process with a minimal number of
  67. Xcomparisons. There are two separate functions realising these two steps,
  68. Xnamely mktd1() and qsearch(). The shift table has to be declared by the
  69. Xcaller and is passed to mktd1() and to qsearch() as well. The allows a
  70. Xsimultaneous searching for several patterns.
  71. XTypically one should generate the shift table once for a given pattern
  72. Xstring and then use it to search for the pattern in a number of text
  73. Xstrings. 
  74. X
  75. XAnother realisation of 'quick search' has been posted to comp.sources.misc
  76. X(v14i074) by gwyn@smoke.brl.mil (Doug Gwyn) to code the usual strstr()
  77. Xlibrary function. My implementation differs from Gwyn's approach as
  78. Xfollows:
  79. X1. Before initialising the shift table, the length of the pattern string
  80. X   is calculated first, see mktd1(). This avoids stepping through the
  81. X   whole shift table (having 256 elements) twice, as done by the Gwyn
  82. X   code. Because initialising the shift table is always rather costly,
  83. X   a quick search based version of the standard strstr() seems useless.
  84. X2. A pre-calculation of text string length is done by qsearch() before
  85. X   the actual matching process begins. The method given by Gwyn avoids
  86. X   this pre-calculation by recognizing that text characters being compared
  87. X   with corresponding pattern characters need not be checked against
  88. X   '\0' too. This results in a better best case and mid case behaviour.
  89. X   But: pathological combinations of text and pattern strings may produce
  90. X   extreme overhead. I tried
  91. X            text   : abcdxxxxxxxxxx
  92. X            pattern: yzxxxxx
  93. X   to find text characters being tested against '\0' 57 times with a
  94. X   text length of only 14 !
  95. X   That's why I arranged things this way: if for some reason the length 
  96. X   of the text string is known, you can pass it to qsearch() via
  97. X   parameter 'tlen', else simply pass 0.
  98. SHAR_EOF
  99. $TOUCH -am 1012144792 qsearch.txt &&
  100. chmod 0644 qsearch.txt ||
  101. echo "restore of qsearch.txt failed"
  102. set `wc -c qsearch.txt`;Wc_c=$1
  103. if test "$Wc_c" != "2749"; then
  104.     echo original size 2749, current size $Wc_c
  105. fi
  106. # ============= qsearch.c ==============
  107. echo "x - extracting qsearch.c (Text)"
  108. sed 's/^X//' << 'SHAR_EOF' > qsearch.c &&
  109. X
  110. X/*
  111. X * qsearch.c: fast substring matching in forward and backward direction
  112. X * 9-Oct-1992, Guido Gronek
  113. X */
  114. X
  115. X#include <stdlib.h>
  116. X#include <string.h>
  117. X#include <limits.h>
  118. X
  119. X#include "qsearch.h"
  120. X
  121. X#define TEST
  122. X
  123. Xstatic size_t Plen;                      /* length of pattern */
  124. X
  125. X
  126. X/*
  127. X * generate shift table from pattern string
  128. X * out: address of the initialised shift table
  129. X */
  130. Xsize_t *mktd1(pstr, reverse, td1)
  131. Xconst char *pstr;                   /* pattern string */
  132. Xint reverse;                        /* reverse order TRUE/FALSE */
  133. Xshifttab td1;                       /* the caller-supplied shift table */
  134. X{
  135. X  int c;
  136. X  size_t m;
  137. X  const char *p;
  138. X  size_t * shift;
  139. X
  140. X  for (p = pstr; *p; ++p)
  141. X    ;
  142. X  Plen = m = p - pstr;              /* length of pattern */
  143. X
  144. X  for( ++m, shift = td1, c = UCHAR_MAX+1; --c >=0; )
  145. X    *shift++ = m;         /* initialize shift table with Plen + 1 */
  146. X
  147. X  if (reverse)
  148. X    for (shift = td1; p>pstr; )     /* scan pattern right to left */
  149. X      shift[*--p] = --m;
  150. X  else
  151. X    for (shift = td1, p = pstr; *p; ) /* scan pattern left to right */
  152. X      shift[*p++] = --m;
  153. X
  154. X  return td1;
  155. X}
  156. X
  157. X
  158. X/* Quicksearch for a pattern in text
  159. X * out: address of the substring in the text or 0 if none
  160. X */
  161. Xchar *qsearch(pstr, text, td1, reverse, tlen)
  162. Xconst char *pstr;                 /* pattern string */
  163. Xconst char *text;                 /* text */
  164. Xconst size_t *td1;                /* shift table ASUMED INITIALISED */
  165. Xint reverse;                      /* reverse order TRUE/FALSE */
  166. Xsize_t tlen;                      /* text string length if > 0 */
  167. X{
  168. X  register const char *p, *t, *tx;
  169. X  const char *txe;
  170. X  size_t m;
  171. X
  172. X  if ( pstr==0 || text==0 )
  173. X    return 0;
  174. X
  175. X  m = Plen;
  176. X  if (tlen > 0)               /* length of text string supported */
  177. X    txe = text + tlen;
  178. X  else
  179. X  {
  180. X    tx = text;
  181. X    while (*tx++)
  182. X      ;
  183. X    txe = --tx;
  184. X  }
  185. X  if (reverse)
  186. X  {
  187. X    tx = txe - m;                  /* rightmost possible match */
  188. X    while ( tx >= text )
  189. X    {
  190. X      p = pstr; t = tx;
  191. X      do
  192. X      {
  193. X        if (*p == 0)               /* pattern scanned completely */
  194. X          return (char *)tx;
  195. X      } while ( *p++ == *t++ );    /* break if mismatch */
  196. X      if ( tx>text )
  197. X          tx -= td1[*(tx-1)];      /* shift to previous text location */
  198. X      else
  199. X        break;
  200. X    }
  201. X  }
  202. X  else
  203. X  {
  204. X    tx = text;
  205. X    while ( tx + m <= txe )
  206. X    {
  207. X      p = pstr; t = tx;
  208. X      do
  209. X      {
  210. X        if (*p == 0)               /*  pattern scanned completely */
  211. X          return (char *)tx;
  212. X      } while ( *p++ == *t++ );    /* break if mismatch */
  213. X      tx += td1[*(tx+m)];          /* shift to next text location */
  214. X    }
  215. X  }
  216. X  return 0;
  217. X}
  218. X
  219. X
  220. X#ifdef TEST
  221. X#include <stdio.h>
  222. X#include <string.h>
  223. X
  224. X#define USAGE "usage: qsearch [-r] text pattern\n"
  225. X
  226. Xchar *strsearch(const char *pstr, const char *text, int reverse);
  227. X
  228. X
  229. Xchar *strsearch(text, pstr, reverse)
  230. Xconst char *text;
  231. Xconst char *pstr;
  232. Xint reverse;
  233. X{
  234. X  static shifttab shift;
  235. X
  236. X  return qsearch( pstr, text, mktd1(pstr,reverse,shift), reverse, 0 );
  237. X}
  238. X
  239. X
  240. Xint main( argc, argv )
  241. Xint  argc;
  242. Xchar *argv[];
  243. X{
  244. X  register char *p;
  245. X  int reverse;
  246. X
  247. X  if ( argc < 3 )
  248. X  {
  249. X    fprintf(stderr, USAGE);
  250. X    exit(1);
  251. X  }
  252. X  if ( (reverse = strcmp(argv[1],"-r") ==0) !=0 && argc < 4 )
  253. X  {
  254. X    fprintf(stderr, USAGE);
  255. X    exit(1);
  256. X  }
  257. X
  258. X  p = reverse ? strsearch(argv[2], argv[3], 1) :  
  259. X                strsearch(argv[1], argv[2], 0);
  260. X  if ( p == 0 )
  261. X    printf("pattern not found\n");
  262. X  else
  263. X    printf("pattern start: %s\n", p);
  264. X  return 0;
  265. X}
  266. X
  267. X#endif
  268. SHAR_EOF
  269. $TOUCH -am 1012222392 qsearch.c &&
  270. chmod 0644 qsearch.c ||
  271. echo "restore of qsearch.c failed"
  272. set `wc -c qsearch.c`;Wc_c=$1
  273. if test "$Wc_c" != "3482"; then
  274.     echo original size 3482, current size $Wc_c
  275. fi
  276. # ============= qsearch.h ==============
  277. echo "x - extracting qsearch.h (Text)"
  278. sed 's/^X//' << 'SHAR_EOF' > qsearch.h &&
  279. X/*
  280. X * qsearch.h: header file for qsearch.c
  281. X * 9-Oct-1992, Guido Gronek
  282. X */
  283. X
  284. Xtypedef size_t shifttab[UCHAR_MAX+1];   /* shift table */
  285. X
  286. Xextern size_t qs_tlen;
  287. X
  288. Xsize_t * mktd1(const char *pstr, int reverse, shifttab td1);
  289. Xchar *qsearch(const char *pstr, const char *text, const size_t *td1, int reverse, size_t tlen);
  290. SHAR_EOF
  291. $TOUCH -am 1012222392 qsearch.h &&
  292. chmod 0644 qsearch.h ||
  293. echo "restore of qsearch.h failed"
  294. set `wc -c qsearch.h`;Wc_c=$1
  295. if test "$Wc_c" != "316"; then
  296.     echo original size 316, current size $Wc_c
  297. fi
  298. exit 0
  299.  
  300.  
  301. exit 0 # Just in case...
  302.