📄 fts3_porter.c
字号:
/*** 2006 September 30**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.***************************************************************************** Implementation of the full-text-search tokenizer that implements** a Porter stemmer.*//*** The code in this file is only compiled if:**** * The FTS3 module is being built as an extension** (in which case SQLITE_CORE is not defined), or**** * The FTS3 module is being built into the core of** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).*/#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)#include <assert.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <ctype.h>#include "fts3_tokenizer.h"/*** Class derived from sqlite3_tokenizer*/typedef struct porter_tokenizer { sqlite3_tokenizer base; /* Base class */} porter_tokenizer;/*** Class derived from sqlit3_tokenizer_cursor*/typedef struct porter_tokenizer_cursor { sqlite3_tokenizer_cursor base; const char *zInput; /* input we are tokenizing */ int nInput; /* size of the input */ int iOffset; /* current position in zInput */ int iToken; /* index of next token to be returned */ char *zToken; /* storage for current token */ int nAllocated; /* space allocated to zToken buffer */} porter_tokenizer_cursor;/* Forward declaration */static const sqlite3_tokenizer_module porterTokenizerModule;/*** Create a new tokenizer instance.*/static int porterCreate( int argc, const char * const *argv, sqlite3_tokenizer **ppTokenizer){ porter_tokenizer *t; t = (porter_tokenizer *) sqlite3_malloc(sizeof(*t)); if( t==NULL ) return SQLITE_NOMEM; memset(t, 0, sizeof(*t)); *ppTokenizer = &t->base; return SQLITE_OK;}/*** Destroy a tokenizer*/static int porterDestroy(sqlite3_tokenizer *pTokenizer){ sqlite3_free(pTokenizer); return SQLITE_OK;}/*** Prepare to begin tokenizing a particular string. The input** string to be tokenized is zInput[0..nInput-1]. A cursor** used to incrementally tokenize this string is returned in ** *ppCursor.*/static int porterOpen( sqlite3_tokenizer *pTokenizer, /* The tokenizer */ const char *zInput, int nInput, /* String to be tokenized */ sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */){ porter_tokenizer_cursor *c; c = (porter_tokenizer_cursor *) sqlite3_malloc(sizeof(*c)); if( c==NULL ) return SQLITE_NOMEM; c->zInput = zInput; if( zInput==0 ){ c->nInput = 0; }else if( nInput<0 ){ c->nInput = (int)strlen(zInput); }else{ c->nInput = nInput; } c->iOffset = 0; /* start tokenizing at the beginning */ c->iToken = 0; c->zToken = NULL; /* no space allocated, yet. */ c->nAllocated = 0; *ppCursor = &c->base; return SQLITE_OK;}/*** Close a tokenization cursor previously opened by a call to** porterOpen() above.*/static int porterClose(sqlite3_tokenizer_cursor *pCursor){ porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; sqlite3_free(c->zToken); sqlite3_free(c); return SQLITE_OK;}/*** Vowel or consonant*/static const char cType[] = { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 2, 1};/*** isConsonant() and isVowel() determine if their first character in** the string they point to is a consonant or a vowel, according** to Porter ruls. **** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'.** 'Y' is a consonant unless it follows another consonant,** in which case it is a vowel.**** In these routine, the letters are in reverse order. So the 'y' rule** is that 'y' is a consonant unless it is followed by another** consonent.*/static int isVowel(const char*);static int isConsonant(const char *z){ int j; char x = *z; if( x==0 ) return 0; assert( x>='a' && x<='z' ); j = cType[x-'a']; if( j<2 ) return j; return z[1]==0 || isVowel(z + 1);}static int isVowel(const char *z){ int j; char x = *z; if( x==0 ) return 0; assert( x>='a' && x<='z' ); j = cType[x-'a']; if( j<2 ) return 1-j; return isConsonant(z + 1);}/*** Let any sequence of one or more vowels be represented by V and let** C be sequence of one or more consonants. Then every word can be** represented as:**** [C] (VC){m} [V]**** In prose: A word is an optional consonant followed by zero or** vowel-consonant pairs followed by an optional vowel. "m" is the** number of vowel consonant pairs. This routine computes the value** of m for the first i bytes of a word.**** Return true if the m-value for z is 1 or more. In other words,** return true if z contains at least one vowel that is followed** by a consonant.**** In this routine z[] is in reverse order. So we are really looking** for an instance of of a consonant followed by a vowel.*/static int m_gt_0(const char *z){ while( isVowel(z) ){ z++; } if( *z==0 ) return 0; while( isConsonant(z) ){ z++; } return *z!=0;}/* Like mgt0 above except we are looking for a value of m which is** exactly 1*/static int m_eq_1(const char *z){ while( isVowel(z) ){ z++; } if( *z==0 ) return 0; while( isConsonant(z) ){ z++; } if( *z==0 ) return 0; while( isVowel(z) ){ z++; } if( *z==0 ) return 1; while( isConsonant(z) ){ z++; } return *z==0;}/* Like mgt0 above except we are looking for a value of m>1 instead** or m>0*/static int m_gt_1(const char *z){ while( isVowel(z) ){ z++; } if( *z==0 ) return 0; while( isConsonant(z) ){ z++; } if( *z==0 ) return 0; while( isVowel(z) ){ z++; } if( *z==0 ) return 0; while( isConsonant(z) ){ z++; } return *z!=0;}/*** Return TRUE if there is a vowel anywhere within z[0..n-1]*/static int hasVowel(const char *z){ while( isConsonant(z) ){ z++; } return *z!=0;}/*** Return TRUE if the word ends in a double consonant.**** The text is reversed here. So we are really looking at** the first two characters of z[].*/static int doubleConsonant(const char *z){ return isConsonant(z) && z[0]==z[1] && isConsonant(z+1);}/*** Return TRUE if the word ends with three letters which** are consonant-vowel-consonent and where the final consonant** is not 'w', 'x', or 'y'.**** The word is reversed here. So we are really checking the** first three letters and the first one cannot be in [wxy].*/static int star_oh(const char *z){ return z[0]!=0 && isConsonant(z) && z[0]!='w' && z[0]!='x' && z[0]!='y' && z[1]!=0 && isVowel(z+1) && z[2]!=0 && isConsonant(z+2);}/*** If the word ends with zFrom and xCond() is true for the stem** of the word that preceeds the zFrom ending, then change the ** ending to zTo.**** The input word *pz and zFrom are both in reverse order. zTo** is in normal order. **** Return TRUE if zFrom matches. Return FALSE if zFrom does not** match. Not that TRUE is returned even if xCond() fails and** no substitution occurs.*/static int stem( char **pz, /* The word being stemmed (Reversed) */ const char *zFrom, /* If the ending matches this... (Reversed) */ const char *zTo, /* ... change the ending to this (not reversed) */ int (*xCond)(const char*) /* Condition that must be true */){ char *z = *pz; while( *zFrom && *zFrom==*z ){ z++; zFrom++; } if( *zFrom!=0 ) return 0; if( xCond && !xCond(z) ) return 1; while( *zTo ){ *(--z) = *(zTo++); } *pz = z; return 1;}/*** This is the fallback stemmer used when the porter stemmer is** inappropriate. The input word is copied into the output with** US-ASCII case folding. If the input word is too long (more** than 20 bytes if it contains no digits or more than 6 bytes if** it contains digits) then word is truncated to 20 or 6 bytes** by taking 10 or 3 bytes from the beginning and end.*/static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){ int i, mx, j; int hasDigit = 0; for(i=0; i<nIn; i++){ int c = zIn[i]; if( c>='A' && c<='Z' ){ zOut[i] = c - 'A' + 'a'; }else{ if( c>='0' && c<='9' ) hasDigit = 1; zOut[i] = c; } } mx = hasDigit ? 3 : 10; if( nIn>mx*2 ){ for(j=mx, i=nIn-mx; i<nIn; i++, j++){ zOut[j] = zOut[i]; } i = j; } zOut[i] = 0; *pnOut = i;}/*** Stem the input word zIn[0..nIn-1]. Store the output in zOut.** zOut is at least big enough to hold nIn bytes. Write the actual** size of the output word (exclusive of the '\0' terminator) into *pnOut.**** Any upper-case characters in the US-ASCII character set ([A-Z])
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -