前言
这是我第51篇博客,写过了50篇博文了,接下来的时间决定继续在CSDN上交流技术和心得,CSDN也许对我也是一种鼓舞和鞭策吧,因为一旦停下来,我就连14600的排名都保不住了,呵呵,希望继续努力,记载下我成长过程的点滴。
说发散思维是因为,以前太过于拘泥于MFC开发了,最近越来越发现,作为一个未来做大项目的开发人员,决不能仅仅限于MFC,限于windows平台,还有很多好东西要学习,学习一些新的方法和技术来开阔视野,总是有帮助的,避免局限于某种语言和平台。
正文
在做一个简单的电子书阅读器的过程中,需要搜索一本电子书的章节,然后自动形成一个目录。
在解决目录生成时我以前用到的方法是:内存映射文件+多线程+关键字搜索的线程函数在MFC平台下,性能很好,但略显麻烦,偶尔还会出错。最近接触的新知识点比较多,于是就强调下发散思维,本文就javascript、C语言、perl、java四种语言如何处理目录搜索来说明很重要的一点思维习惯:发散思维, 尝试从不同途径获得启发。
方法一:javascript正则表达式解决目录搜索
测试程序源码如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE> 正则表达式 </TITLE>
<script type="text/javascript">
{
function GetContent()
{
var s=document.getElementById("input").value;
if(s==null || s=="")
{
alert("请填写内容");
return;
}
//新建正则表达式
var patt=new RegExp("第[0-9一二三四五六七八九十百千零]{1,12}[章|节|回|篇|卷|部分].*[\r|\n|\x20]","g");
do
{
result=patt.exec(s);
if(result!="" && result!=null)
{
document.write(result);
document.write("<br>");
}
}
while (result!=null)
document.write("........搜索完毕........");
}
}
</script>
</HEAD>
<BODY>
<textarea rows="20" cols="80" id="input"></textarea><br><br>
<input type="button" value="获取匹配串" οnclick="GetContent()">
</BODY>
</HTML>
程序运行效果如下图1、图2所示:
图1 javascript示例程序操作界面
图2 生成目录效果
方法二:C语言使用第三方DEELX正则表达式库
使用DEELX Regular Expression Engine,利用c语言实现(代码在vs2010中测试通过,部分函数是新版本的,vc6.0中并不支持)代码如下所示:
// RegExp.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include "deelx.h" //包含DEELX Regular Expression Engine 头文件
#define DEFAULT_SIZE 1024
typedef enum Error
{
ERROR_FILEOPEN=0,
ERROR_FILESAVE=1,
ERROR_NOMATCH=2,
ERROR_MEMORY=3,
ERROR_NONE=4
}ErrorCode;
//在Unicode宽字节类型文件中查找
ErrorCode SearinWChFile(const char *Srcfilename,const char *Savefilename);
ANSI文件中查找
ErrorCode SearinMBFile(const char *Srcfilename,const char *Savefilename);
//获取错误描述字符串
void GetErrorDescription(ErrorCode err,char* pszstr);
//判别文件编码类型
bool IsFileUnicode(const char *pszFilePath);
int _tmain(int argc, _TCHAR* argv[])
{
char src[255],des[255];
ErrorCode err;
char description[100];
printf("input source file full-path:\n");
scanf("%s",src);
printf("input content save file full-path:\n");
scanf("%s",des);
if(IsFileUnicode(src))
{
printf("%s is Unicode File!\n",src);
err=SearinWChFile(src,des);
}
else
{
printf("%s is not Unicode File or error occurred!\n",src);
err=SearinMBFile(src,des);
}
GetErrorDescription(err,description);
printf("%s\n",description);
if(err!=ERROR_NONE)
return 0;
else
printf("content is saved in the following path:\n%s\n",des);
return 0;
}
//在Unicode宽字节类型文件中查找
ErrorCode SearinWChFile(const char *Srcfilename,const char *Savefilename)
{
wchar_t ptn[]=L"第[0-9一二三四五六七八九十百千零]{1,12}[章|节|回|篇|卷|部分].*[\n|\r|\x20]\0";
wchar_t *pTemp;
wchar_t head = 0xFEFF;
wchar_t Rowcharacter[]= L"\r\n";
CRegexpT <wchar_t> regexp(ptn);
FILE *pStreamIn=NULL;
FILE *pStreamOut=NULL;
bool bFind=false;
//打开文件
if( 0!=fopen_s(&pStreamIn,Srcfilename,"rb"))
return ERROR_FILEOPEN;
if(0!=fopen_s(&pStreamOut,Savefilename,"wb"))
return ERROR_FILESAVE;
fwrite(&head,sizeof(wchar_t),1,pStreamOut);//写入Unicode文件头
//分配空间
wchar_t *pszSrc=new wchar_t[DEFAULT_SIZE+1];
if(pszSrc==NULL)
return ERROR_MEMORY;
//获取宽字符串长度 单位为宽字符
int cur=ftell(pStreamIn);
fseek(pStreamIn,0,SEEK_END);
int end=ftell(pStreamIn);
int WChlen=(end-cur)/2;
fseek(pStreamIn,0,SEEK_SET);
while(WChlen>0)
{
memset(pszSrc,0,(DEFAULT_SIZE+1)*sizeof(wchar_t));
fread((void*) pszSrc, sizeof(wchar_t),DEFAULT_SIZE, pStreamIn);
//查找第一个
MatchResult result = regexp.Match(pszSrc);
while(result.IsMatched())
{
bFind=true;
// 将查找结果写入文件
pTemp=pszSrc + result.GetStart();
size_t len=result.GetEnd() - result.GetStart();
if(pStreamOut!=NULL)
{
fwrite(pTemp,sizeof(wchar_t),len,pStreamOut);
fwrite(Rowcharacter,sizeof(wchar_t),wcslen(Rowcharacter),pStreamOut);
}
// 继续查找
result = regexp.Match(pszSrc, result.GetEnd());
}
if(WChlen>0)
WChlen-=DEFAULT_SIZE;
}
//资源清理
fclose(pStreamIn);
fclose(pStreamOut);
delete[] pszSrc;
if(!bFind)
return ERROR_NOMATCH;
return ERROR_NONE;
}
//ANSI文件中查找
ErrorCode SearinMBFile(const char *Srcfilename,const char *Savefilename)
{
char ptn[]="第[0-9一二三四五六七八九十百千零]{1,12}[章|节|回|篇|卷|部分].*[\n|\r|\x20]\0";
char *pTemp;
CRegexpT <char> regexp(ptn);
FILE *pStreamIn=NULL;
FILE *pStreamOut=NULL;
bool bFind=false;
//打开文件
if( 0!=fopen_s(&pStreamIn,Srcfilename,"r"))
return ERROR_FILEOPEN;
if(0!=fopen_s(&pStreamOut,Savefilename,"w"))
return ERROR_FILESAVE;
//申请空间
char *pszSrc=new char[DEFAULT_SIZE+1];
if(pszSrc==NULL)
return ERROR_MEMORY;
//通过feof判断文件结束
while(!feof(pStreamIn))
{
memset(pszSrc,0,(DEFAULT_SIZE+1)*sizeof(char));
fread((void*) pszSrc, sizeof(char),DEFAULT_SIZE, pStreamIn);
//查找第一个
MatchResult result = regexp.Match(pszSrc);
while(result.IsMatched())
{
bFind=true;
// 查找结果写入文件
pTemp=pszSrc + result.GetStart();
size_t len=result.GetEnd() - result.GetStart();
if(pStreamOut!=NULL)
fwrite(pTemp,sizeof(char),len,pStreamOut);
// 继续查找
result = regexp.Match(pszSrc, result.GetEnd());
}
}
//资源清理
fclose(pStreamIn);
fclose(pStreamOut);
delete[] pszSrc;
if(!bFind)
return ERROR_NOMATCH;
return ERROR_NONE;
}
//获取错误描述字符串
void GetErrorDescription(ErrorCode err,char* pszstr)
{
switch(err)
{
case ERROR_FILEOPEN:
{
char szDesp[]="The file you wanna to search can't open as expected!\0";
strcpy_s(pszstr,sizeof(szDesp)/sizeof(szDesp[0]),szDesp);
break;
}
case ERROR_FILESAVE:
{
char szDesp[]="The file to save content, can't open as expected!\0";
strcpy_s(pszstr,sizeof(szDesp)/sizeof(szDesp[0]),szDesp);
break;
}
case ERROR_NOMATCH:
{
char szDesp[]="there is no matching results as expected!\n\0";
strcpy_s(pszstr,sizeof(szDesp)/sizeof(szDesp[0]),szDesp);
break;
}
case ERROR_MEMORY:
{
char szDesp[]="Memory exception occurred while searching for the content!\0";
strcpy_s(pszstr,sizeof(szDesp)/sizeof(szDesp[0]),szDesp);
break;
}
case ERROR_NONE:
{
char szDesp[]="you have get the content as expected!\0";
strcpy_s(pszstr,sizeof(szDesp)/sizeof(szDesp[0]),szDesp);
break;
}
default:
break;
}
}
//判别文件编码类型
bool IsFileUnicode(const char *pszFilePath)
{
unsigned char buf[2];
FILE *pStreamIn=NULL;
if( 0!=fopen_s(&pStreamIn,pszFilePath,"rb"))
return false;
memset(buf,0,2*sizeof(unsigned char));
fread((void*) buf, sizeof(unsigned char),2, pStreamIn);
fclose(pStreamIn);
if(buf[0]==0xFF &&buf[1]==0xFE)
return true ;
else
return false;
}
这里只是为了说明三种语言正则表达式的特性,关于Unicode编码等问题在此不做讨论。
运行效果如下图3、图4所示:
图3 c语言生成目录操作界面
图4 c语言生成目录效果
对于上述程序,运行unicode文件生成的目录文件非常完美,但是对于ANSI文件还存在瑕疵,不知道为什么同一正则表达式对同一个引擎会带来不同结果。这点可能就是每个引擎设计的问题后者是正则表达式需要针对性的稍作修改。
方法三:perl脚本正则表达式生成目录
perl生成目录代码为:
#!/usr/bin/perl
use strict;
use warnings;
my $line;
print "input source file full-path:\n";
my $src=<STDIN>;#源文件路径
printf "input content save file full-path:\n";
my $des=<STDIN>;#保存文件路径
#打开文件
open STREAMIN,"< $src" or die "can not open file:$src:$!";
open STREAMOUT,"> $des" or die "can not save content file: $des:$!";
#寻找匹配项目
my $bfound=0;
while(defined($line=<STREAMIN>))
{
if( $line =~/第[0-9一二三四五六七八九十百千零]{1,12}[章|节|回|篇|卷|部分].*\s/ )
{
$bfound=1;
syswrite(STREAMOUT,"$line\r\n");#将匹配项目写入文件
}
}
if(!$bfound)
{
print "no matching results!";
}
#关闭文件
close(STREAMIN);
close(STREAMOUT);
perl语言正则表达式生成目录运行效果如下图5、图6所示:
图5 perl生成目录操作界面
图6 perl生成目录效果
perl按照上述模式匹配时会产生些瑕疵,目前还未完全解决。
方法四: 利用java搜索目录(2013年更新)
java生成目录测试代码如下:
package com.learningjava;
import java.io.*;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This programe try to illuminate using regex in java
*
* Note,Capturing Matcher groups are numbered
* by counting their opening parentheses from left to right.
* The real group index from index 1 .
*
* @author wangdq
* 2011-11-15
*/
public class GetContentDemo {
public static void main(String[] args) {
try {
//read file via BufferedReader
Scanner scanner = new Scanner(new BufferedReader(new FileReader(new File("三国演义.txt"))));
//prepare regular expression
String regex = "(([\\s|\\S]*)(第[0-9一二三四五六七八九十百千零]+[章|节|回|篇|卷|部分])([\\s|\\S]*))";
Pattern pattern = Pattern.compile(regex);
int lineCnt = 0;
//find matched string and print it
while(scanner.hasNextLine()) {
Matcher matcher = pattern.matcher(scanner.nextLine());
if(matcher.matches()) {
lineCnt++;
System.out.format("%-8s\t%s%n",matcher.group(3),matcher.group(4));
}
}
scanner.close();
System.out.println("analyze end,found "+lineCnt+" lines");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果如下图7所示:
图7 java正则表达式生成目录
小结
总述,上述几种语言均利用了正则表达式去解决目录搜索问题,各有特点,javascript语言支持正则表达式的效果最好;c语言和perl语言支持正则表达式会出现一些问题,可能是正则表达式的模式定义与javascript有些不同,也可能是语言本身的特点;java处理中文匹配时注意其String类型字符串编码为unicode类型。
在解决目录生成问题上尝试了利用多么语言的正则表达式来解决,熟悉了多么语言的使用特点,开阔了解决问题的眼界,很有益处。可以肯定在其他语言中使用正则表达式可定能行,在此不作深究。
当然文中的正则表达式是否完全正确或者有何改进的地方,还需要进一步学习。