DB2中实现正则表达式.docx
《DB2中实现正则表达式.docx》由会员分享,可在线阅读,更多相关《DB2中实现正则表达式.docx(23页珍藏版)》请在冰豆网上搜索。
DB2中实现正则表达式
DB2中实现正则表达式
(1)
2005-12-0110:
00出处:
IBM中国
【导读】本文简略地介绍了正则表达式以及DB2中可用的字符串比较和匹配功能。
还描述了为什么正则表达式的强大功能是如此有用。
正则表达式
正则表达式用于查找和替换字符串中的模式。
正则表达式是用某种语法定义的,正则表达式引擎采用这种语法并将它与字符串进行比较。
引擎返回字符串是否与语法匹配的指示;也即,该字符串是否包含能够从该语法派生的子串。
此外,引擎还能够返回匹配的子串。
术语“模式(pattern)”用来表示语法。
最基本的模式仅由单个字母组成。
当与该模式进行比较时,包含这个字母的字符串就是一个“匹配”。
例如,如果模式是“a”,则字符串“abcd”就是一个匹配,而字符串“xyz”则不是。
正则表达式的强大功能来自于预定义的运算符(也称为元字符),它们可以用很小的空间来表示模式。
根据“方言”和受支持的功能,可以使用不同的元字符。
通常,其中的一些可用字符如下:
|—二中择一
[]—分组
*—多次出现(也匹配零次出现)
+—多次出现(至少一次)
?
—随意的出现次数
\\\\—反斜杠
不同的系统实现了常用正则表达式的各种扩展。
编程语言Perl中使用的正则表达式支持进一步的缩写。
本文中所用的库实现了这些扩展。
下面摘录了其中部分可以在Perl正则表达式语言中使用的缩写:
\\s—任意空白字符
\\w—任意字母数字字符
\\d—任意数字字符
另一个更高级的示例是模式“[A-Z]*=([0-9]|0x00);”。
与这个模式相匹配的字符串包含这样的子串:
它由几个大写字母、后面跟上一个空格、一个等号、另一个空格,然后是一个数字或字符串“0x00”组成。
该子串的最后一个字符必须是分号。
使用Perl,这个模式可以表示为“\\w*=(\\d|0x00);”。
“NM=0x00;”和“X=7;”是两个可以与该模式匹配的字符串。
但字符串“Z=123;”不能匹配,因为123是由三个数字所组成的。
DB2中的字符串匹配
除了Extender以外,DB2还允许几种用于文本比较的函数和运算符。
但那些函数和运算符要么在用于模式匹配的功能方面有限制,要么就是会给可能使用它们的查询带来复杂性。
这里简要地摘录几个可用的功能:
=或<>谓词:
逐字符地比较两个字符串是否相等。
LIKE谓词:
使用通配符的基本模式匹配。
LOCATE函数:
在字符串中查找子串。
尽管也可以用SQL运算符表示模式“[A-Z]*=([0-9]|0x00);”,但那样会很麻烦。
例如,下列SELECT语句的WHERE子句中所使用的谓词会匹配字符串“str”中等号之后的部分,如清单1所示:
清单1.使用LIKE匹配模式
SELECTstr
FROMstrTable
WHERE(strLIKE'%=0;%'ORstrLIKE'%=1;%'ORstrLIKE'%=2;%'
ORstrLIKE'%=3;%'ORstrLIKE'%=4;%'ORstrLIKE'%=5;%'
ORstrLIKE'%=7;%'ORstrLIKE'%=7;%'ORstrLIKE'%=8;%'
ORstrLIKE'%=9;%'ORstrLIKE'%=0x00;%')
这增加了可以匹配“[A-Z]*”子模式的谓词的复杂度,这可以使用对整个字符串进行迭代并进行逐字符比较的函数来完成,但您会发现使用内置功能既冗长又复杂。
示例方案
让我们定义下列清单(清单2)并插入几行:
清单2.创建我们的样本表
CREATETABLEstrTable(c1INTEGER,strVARCHAR(500));
INSERTINTOstrTableVALUES(1,'sometext;'),
(2,'variable=1234;'),
(3,'var2=''stringvariable'';'),
(4,'xyz='),
(5,'myVar=0x00;'),
(6,'#comment'),
(7,'abc=def');
这个清单及其数据被用于下面的所有示例。
SELECT*FROMstrTable;
C1STR
-----------------------------------------
1sometext;
2variable=1234;
3var2='stringvariable';
4xyz=
5myVar=0x00;
6#comment
7abc=def
7record(s)selected.
实现模式匹配函数
您可以使用DB2的可扩展机制,在SQL语句内使用UDF,以便显著地改善这种情形。
通过定义名为regex1的UDF(它采用模式和字符串作为输入参数),清单1中的WHERE子句现在可以写得象清单3中所示的那样:
清单3.使用regexUDF来简化模式匹配
SELECTstr
FROMstrTable
WHEREregex1('\\w*=(\\d|0x00);',str)=1
在本示例中,使用带有Perl扩展的正则表达式来匹配完整的模式,而不仅仅是清单1中给出的LIKE谓词所对应的部分模式。
正如您所看到的,使用函数来为该模式编写谓词比用LIKE谓词表示同样的语义要容易得多。
实现UDF
在我的示例实现中,我选择了现有的名为PCRE(Perl兼容的正则表达式,Perl-compatibleregularexpression)的模式匹配引擎。
该引擎提供了用来处理模式和执行匹配的CAPI。
该引擎和查询中所用的SQL语言之间“缺失的部分”是UDF。
该UDF由两部分组成:
在数据库中创建(或注册)该函数的CREATEFUNCTION语句。
该函数的主体,它实现了用于正则表达式匹配引擎的CAPI调用的封装器
清单4显示了用于创建该函数的SQL语句。
清单4.注册regex1函数
CREATEFUNCTIONregex1(patternVARCHAR(2048),stringCLOB(10M))
RETURNSINTEGER
SPECIFICregexSimple
EXTERNALNAME'regexUdf!
regexpSimple'
LANGUAGEC
PARAMETERSTYLEDB2SQL
DETERMINISTIC
NOTFENCED
RETURNSNULLONNULLINPUT
NOSQL
NOEXTERNALACTION
ALLOWPARALLEL;
注:
请参阅DB2SQLReference以获取所有子句的详细含义。
可以修改参数的长度以适应您的需求。
我在此处展示某些值并没有任何推荐使用它们的用意。
DB2中实现正则表达式
(2)
2005-12-0110:
00出处:
IBM中国
【导读】本文简略地介绍了正则表达式以及DB2中可用的字符串比较和匹配功能。
还描述了为什么正则表达式的强大功能是如此有用。
第二部分由一小段C代码组成,它实现了UDF入口点。
在查询执行期间,DB2为每个要与模式匹配的行调用这个入口点。
清单5中的示例列出了该代码的清单。
有关pcre_*函数和宏的描述,请参考PCRE库的文档。
有关C代码的编译和共享库的构建,请参考DB2ApplicationDevelopmentGuide。
清单5.实现rege1xUDF入口点的C代码
#include
#include
voidregexpSimple(
//inputparameters
SQLUDF_VARCHAR*pattern,SQLUDF_CLOB*str,
//output
SQLUDF_INTEGER*match,
//nullindicators
SQLUDF_NULLIND*pattern_ind,SQLUDF_NULLIND*str_ind,
SQLUDF_NULLIND*match_ind,
SQLUDF_TRAIL_ARGS)
{
pcre*re=NULL;
constchar*error=NULL;
interrOffset=0;
intrc=0;
//weassumesuccessfulreturn
*match_ind=0;
//compilethepatterntoitsinternalrepresentation
re=pcre_compile(pattern,0/*defaultoptions*/,&error,
&errOffset,NULL);
if(re==NULL){
snprintf(SQLUDF_MSGTX,70,"Regexpcompilationfailedat"
"offset%d:
%s\\n",errOffset,error);
strcpy(SQLUDF_STATE,"38900");
(*pcre_free)(re);
return;
}
//matchthestringagaintsthepattern
rc=pcre_exec(re,NULL,str->data,str->length,0,
0/*defaultoptions*/,NULL,0);
switch(rc){
casePCRE_ERROR_NOMATCH:
*match=0;
break;
casePCRE_ERROR_BADOPTION:
snprintf(SQLUDF_MSGTX,70,"Anunrecognizedbitwassetinthe"
"optionsargument");
strcpy(SQLUDF_STATE,"38901");
break;
casePCRE_ERROR_NOMEMORY:
snprintf(SQLUDF_MSGTX,70,"Notenoughmemoryavailable.");
strcpy(SQLUDF_STATE,"38902");
break;
default:
if(rc<0){
snprintf(SQLUDF_MSGTX,70,"Aregexpmatcherror"
"occured:
%d",rc);
strcpy(SQLUDF_STATE,"38903");
}
else{
*match=1;
}
break;
}
//cleanup
(*pcre_free)(re);
return;
}
用法示例
下列查询试图从表strTable中找出包含注释文本的所有字符串。
注释以“#”开头,所以模式是“#”后跟非空文本。
SELECTc1,str
FROMstrTable
WHEREregex1('#\\s*\\w+',str)=1;
结果只包含c1=6的行。
C1STR
------------------------------------
6#comment;
1record(s)selected.
在第二个示例中,我们试图找到这种赋值形式的字符串;即“text=text”。
为了进一步缩小范围,我们只查找那些右端为数值的赋值。
将十六进制表示法作为有效数值对待。
SELECTc1,str
FROMstrTable
WHEREregex1('\\w+\\s*=\\s*(\\d+|0x\\d\\d)',str)=1;
除了c1为2或5的两行以外,其它行都不包含数值的赋值,因此不会出现在结果中:
C1STR
------------------------------------
2variable=1234;
5myVar=0x00;
2record(s)selected.
改进性能
尽管上面的函数按照预期的方式工作,但还可以改进它以获得更佳的性能。
注:
函数内部的执行完成得越快,DB2处理整个SQL语句的速度也就越快。
SQL旨在处理多组行,这意味着通常会针对一个模式匹配多个行。
在大多数情况下,模式本身对于整个SQL语句都是不变的;即,它不会随行的更改而更改。
清单5中的C代码展示了对每一行都调用函数pcre_compile(),该函数将给定模式转换成内部表示法。
DB2通过使用所谓的“高速暂存(scratchpad)”提供了在UDF调用之间传递信息的机制。
此外,您可以标识特定调用“类型”;即它是对该UDF的第一次调用、普通调用还是最后一次(最终)调用。
使用高速暂存和调用类型,有可能只对模式编译一次,然后将该已编译模式的内部表示法重用于对该UDF的所有后续调用。
在最后一次调用时,释放在处理期间分配的资源。
如清单6所示,对CREATEFUNCTION语句进行修改,告诉DB2向外部C代码提供高速暂存和调用类型:
清单6.将高速暂存和调用类型添加到CREATEFUNCTION语句
CREATEFUNCTIONregex2(patternVARCHAR(2048),stringCLOB(10M))
RETURNSINTEGER
SPECIFICregexPerf
EXTERNALNAME'regexUdf!
regexpPerf'
LANGUAGEC
PARAMETERSTYLEDB2SQL
DETERMINISTIC
NOTFENCED
RETURNSNULLONNULLINPUT
NOSQL
NOEXTERNALACTION
SCRATCHPAD50
FINALCALL
ALLOWPARALLEL;
UDF入口点看起来很不一样,因为必须改写函数内部的逻辑。
参数方面唯一的更改是使用SQLUDF_TRAIL_ARGS_ALL代替了SQLUDF_TRAIL_ARGS,如清单7所示。
清单7.regex2的CUDF入口点
#include
#include
//datastructuremappedonthescratchpadforeasieruseandaccess
//totheobjects
//thesizeofthescratchpaddefinedintheCREATEFUNCTIONstatement
//mustbeatleastaslargeassizeof(scratchPadMapping)
structscratchPadMapping{
pcre*re;
pcre_extra*extra;
constchar*error;
interrOffset;
};
voidregexpPerf(
//inputparameters
SQLUDF_VARCHAR*pattern,SQLUDF_CLOB*str,
//output
SQLUDF_INTEGER*match,
//nullindicators
SQLUDF_NULLIND*pattern_ind,SQLUDF_NULLIND*str_ind,
SQLUDF_NULLIND*match_ind,
SQLUDF_TRAIL_ARGS_ALL)//SQLUDF_SCRAT&SQLUDF_CALLT
{
intrc=0;
structscratchPadMapping*scratch=NULL;
//mapthebufferofthescratchpadandassumesuccessfulreturn
scratch=(structscratchPadMapping*)SQLUDF_SCRAT->data;
*match_ind=0;
switch(SQLUDF_CALLT){
caseSQLUDF_FIRST_CALL:
//initializedataonthescratchpad
scratch->re=NULL;
scratch->extra=NULL;
scratch->error=NULL;
scratch->errOffset=0;
//compilethepattern(onlyintheFIRSTcall
scratch->re=pcre_compile(pattern,0/*defaultoptions*/,
&scratch->error,&scratch->errOffset,NULL);
if(scratch->re==NULL){
snprintf(SQLUDF_MSGTX,70,"Regexpcompilationfailedat"
"offset%d:
%s\\n",scratch->errOffset,scratch->error);
strcpy(SQLUDF_STATE,"38900");
rc=-1;
break;
}
//furtheranalyzethepattern(mightreturnNULL)
scratch->extra=pcre_study(scratch->re,
0/*defaultoptions*/,&scratch->error);
/*fallthroughtoNORMALcallbecauseDB2expectsaresult
alreadyintheFIRSTcall*/
caseSQLUDF_NORMAL_CALL:
//matchthecurrentstring
rc=pcre_exec(scratch->re,scratch->extra,str->data,
str->length,0,0/*defaultoptions*/,NULL,0);
switch(rc){
casePCRE_ERROR_NOMATCH:
*match=0;
rc=0;
break;
casePCRE_ERROR_BADOPTION:
snprintf(SQLUDF_MSGTX,70,"Anunrecognizedbitwasset"
"intheoptionsargument");
strcpy(SQLUDF_STATE,"38901");
rc=-1;
break;
casePCRE_ERROR_NOMEMORY:
snprintf(SQLUDF_MSGTX,70,"Notenoughmemoryavailable.");
strcpy(SQLUDF_STATE,"38902");
rc=-1;
break;
default:
if(rc<0){
snprintf(SQLUDF_MSGTX,70,"Aregexpmatcherror"
"occured:
%d",rc);
strcpy(SQLUDF_STATE,"38903");
rc=-1;
}
else{
*match=1;
rc=0;
}
break;
}
break;
}
//cleanupinFINALcall,orifweencounteredanerrorin
//theFIRSTcall(DB2willmakeaFINALcallifweencounter
//anerrorinanyNORMALcall)
if(SQLUDF_CALLT==SQLUDF_FINAL_CALL||
(SQLUDF_CALLT==SQLUDF_FIRST_CALL&&rc<0)){
(*pcre_free)(scratch->re);
(*pcre_free)(scratch->extra);
}
return;
}
为了进一步改进该函数的性能,我添加了对函数pcre_study()的调用,该函数是由模式匹配引擎提供的。
该函数进一步分析了该模式,并将额外的信息存储在独立的结构中。
然后,在实际的匹配期间使用这些额外的信息来加快处理速度。
通过使用一个非常简单的模式和大约4000行的表,我获得了5%的执行时间的改善。
当然,模式越复杂,差异将越显著。
DB2中实现正则表达式(3)
2005-12-0110:
00出处:
IBM中国
【导读】本文简略地介绍了正则表达式以及DB2中可用的字符串比较和匹配功能。
还描述了为什么正则表达式的强大功能是如此有用。
先前提到该实现假定模式在处理期间不会随行的不同而更改。
当然,如果模式确实更改了,您可以进行少量的改写以再次编译一个模式。
要这样做,有必要跟踪当前(已编译的)模式并在每次调用中将它与所提供的模式进行比较。
也可以在高速暂存中维护当前模式。
但必须将它复制到独立的缓冲区,并且不能通过指针模式直接引用它,因为这个指针或它所引用的数据可能会更改或变为无效。
至于相应的代码更改,就当作练习留给读者了。
返回匹配子串
大多数模式匹配引擎提供了一种方法,返回与指定模式或其一部分相匹配的子串。
如果想在SQL中使用这种能力,则必须使用不同的方法来实现匹配函数。
给定的字符串可能包含不止一个匹配的子串。
例如,当解析类似“abc=123;”或“def='sometext';”这样的字符串时,用户可能会希望检索由等号分隔的两个子串。
您可以使用模式“\\w+\\s*=\\s*(\\d+|'[\\w\\s]*');”来表示适用于该字符串的语法规则。
Perl兼容的正则表达式允许您捕