ExtractStrings()奇怪的删行规则~ (100分)

  • 主题发起人 主题发起人 aimingoo
  • 开始时间 开始时间
A

aimingoo

Unregistered / Unconfirmed
GUEST, unregistred user!
哈。我有一天做字处理的时候,用到这个破函数,用来用去,用得我一头包。就把源代
码调出来,然后写了一堆Test,得到这么下面的一篇看起来令人昏倒的东东。

一直怕打击大家对extractstrings()的兴趣,所以从来没公开的贴过。哈哈哈~~~

我想看完之后,估计会有很多人痛骂extractstrings()的算法。象beta和creation-zy
这样的人,可能就会立志重写extractstrings()算法了。哈哈哈。

--------------

ExtractStrings()函数的使用

在Classes单元中, ExtractStrings()是这样声明的:
---------------------------------------------------------------------------
function ExtractStrings(Separators, WhiteSpace: TSysCharSet;
Content: PChar
Strings: TStrings): Integer;
---------------------------------------------------------------------------
返回的结果值是Strings.count.

ExtractStrings()函数的功能类型于JS中的split(), 但是ExtractStrings()也存在一
些特殊的地方. 其中, 它的参数:
Separators : 指定分隔符的集合
WhiteSpace : 指定跳过的字符集. 这些字符如果出现在分隔符的后面(下一行的开
始), 将被忽略.
(*) Separators始终包含[#0, #13, #10, '''', '"']这个子集
WhiteSpace始终
包含[#13, #10]这个子集.

ExtractStrings()函数的特殊之处主要在于它对单引号和双引号的处理上. 由于Separators
始终包含['''','"']这个子集, 意味着这样的特殊性将是不可避免的:

1. 除了最未一行, 通常情况下, 返回结果Strings中不会出现奇数个单引号和双引号.
如果Extract的结果使该行中存在有奇数个单引号和双引号, 则该行被剔除.

2. 返回结果Strings的单行中, 可以出现成对的单引号和双引号.

3. 奇数个单引号和双引号不仅影响到本行, 会导致本行被剔除之外, 还会使后续行可
能被剔除. 引号导致剔除的规则为:
(本行引号数 + 后续x行中的引号数 = 偶数)
or
(后续x行 = 最未一行)
如果条件满足, 则本行至第x-1行将被剔除, 而第x行将被显示, 且第x行中存在奇数个
单引号和双引号.

如果在WhiteSpace中包含了单引号或者双引号, 又将会使上述规则进一步复杂化. 表
现是: 在行前存在的任意个单引号和双引号, 将会按照WhiteSpace的指定规则被剔除,
然后再按Separators的规则进一步的分析.

因此, 这会导致原本有偶数个的行也会被剔除.
 
差點害死人。謝謝。
D的引號!!!
 
真要命,它把引號中的分隔符屏蔽掉了。
aaaa;bb'cc;dd'ee;fff
希望返回
aaaa
bb'cc
dd'ee
fff
結果返回
aaaa
bb'cc;dd'ee
fff

進裡面看了看,它碰到引號後所有的字符都算在一個裡了,直到出來下個引號為止
所以
aaa;bbb'cgg;ioge;goeg;goeg;gorggoi'geoig;gef
這種樣子的串它不管引號中鬧翻天回來的就是
aaa
bbb'cgg;ioge;goeg;goeg;gorggoi'geoig
gef


 
我以前也中过这样的标,
其实TStringList中可以这样使用的:
sl:TStringList;
sl.CommaText := '"aaa","bbb'cgg","goeg'goeg"';
结果是
sl.Strings[0] 为 aaa
sl.Strings[1] 为 bbb'cgg
sl.Strings[2] 为 goeg'goeg
感觉上这个算法是有一定优先级的。
 
function SplitString(pString:Pchar;psubString:PChar):TStringList;
var
nSize,SubStringSize:DWord;
intI,intJ,intK:DWORD;
ts:TStringList;
blnAdd:boolean;//匹配增加字符串
curChar:Char;
strString:string;
stepLen:DWORD;
begin
nSize:=strLen(pString);
SubStringSize:=strLen(PSubString);
ts:=TStringList.Create;
blnAdd:=false;
strstring:='';
steplen:=0;
for inti:=0 to nSize-1 do
begin
if blnAdd=true then
begin
ts.add(strstring);
strstring:='';
blnadd:=false;
end;
if ((PString+intI)^=pSubString^) and (StepLen=0) then
begin
intk:=inti;
for intj:= 0 to SubStringSize-1 do
begin
if ((pString+intk)^=(PSubString+intj)^) and(intk<=nsize-1) then
begin
intk:=intk+1;
end
else
begin
break
//超出字符串长度或不匹配 退出FOR
end;
if intJ=substringSize-1 then
begin
StepLen:=intj+1;
blnAdd:=true;
end;
end;

end;
if blnadd=false then
begin
if steplen=0 then
begin
curChar:=(pString+inti)^;
strstring:=strstring+curchar;
end;
end;
if steplen>0 then steplen:=steplen-1;
if intI=nSize-1 then
begin
ts.add(strstring);
end;
end;
Result:=ts;

end;
例子
strstring:='-x3s-xds-xad-x5-x14-x3-';
subString:='-';
strlst:=TStringlist.Create;
strlst:=splitstring(pchar(strString),Pchar(SubString));
listBox1.Items:=strlst;

另:DELPHI不支持步长超过1的循环,没办法,只能一个个循环了,要不还可以优化一下,其实也可以换成WHILE循环但是懒了,就不优化了。所以代码效率一般。
 
>>Separators始终包含[#0, #13, #10, '''', '"']这个子集

这句话有概念错误。虽然代码中有类似的情况,但再出现引号的情况下。下次循环进来时,在那个条件判断中起作用的是前半部分(就是等引号再次出现,或直接到字符串的结束)。

>>由于Separators始终包含['''','"']这个子集, 意味着这样的特殊性将是不可避免的

所以这个extractstring对引号的特殊处理不在于它那个循环(charnext()那个循环)的条件判断,还在于循环后if tail^ in['''','"'] then这里面的处理。


最后问楼主一个问题。在字符指针的的移动中
P := charnext(P)
与inc(P) 有什么区别吗?
这种操作MS还做了什么好玩的在里面吗?
 
to lichdr,

>>Separators始终包含[#0, #13, #10, '''', '"']这个子集
这句话是针对用户而言的,而不是源代码的执行流程。我的意思是说,Separators的这个特性是用户代码所不能改变的。

>>charnext(P)与inc(P) 有什么区别吗?
哈charnext()是API,这意味着charnext()中,Char类型的宽度取决于OS,而inc()中,Char类型的宽度取决于Delphi对Char在编译期的设定。
在标准的AnsiChar型的Windows上,这看不出区别。但你要知道,M$有基于Unicode Char的Windows。^.^
 
//象beta和creation-zy这样的人,可能就会立志重写extractstrings()算法了。哈哈哈。
^_^ 这次你猜错了,我没有这个打算,因为 jclStrings 里面有 StrToStrings 了:)

本来最近有个新的想法想帖出来,可是老千家上不了网:( 好不容易跑到 yzhshi 这里
来过过瘾,发现还需要点时间整理一下,下次吧:(

对了,你的大作,什么时候才能拜读到啊?:)

 
哦,真是仔细,又学到了一些东西
 
我写的SPLIT函数支持多字符匹配,支持不可见字符,烦劳大家测试一下,谢谢!
function SplitString(pString:Pchar;psubString:PChar):TStringList;
var
nSize,SubStringSize:DWord;
intI,intJ,intK:DWORD;
ts:TStringList;
blnAdd:boolean;//匹配增加字符串
curChar:Char;
strString:string;
strsearchSubStr:string;
blnDoLoop:boolean;
begin
nSize:=strLen(pString);
SubStringSize:=strLen(PSubString);
ts:=TStringList.Create;
blnAdd:=false;
strstring:='';
inti:=0;
blndoloop:=true;
while intI<=(nSize-1) do
begin
if ((PString+intI)^=pSubString^) then
begin
intk:=inti;
strSearchSubStr:='';
for intj:= 0 to SubStringSize-1 do
begin
if ((pString+intk)^=(PSubString+intj)^) and(intk<=nsize-1) then
begin
curchar:=(pstring+intk)^;
intk:=intk+1;
blndoloop:=false;
strsearchSubStr:=strSearchSubStr+Curchar;
end
else
begin
inti:=intk;
strString:=strString+strSearchSubStr;
blndoloop:=false;
break
//超出字符串长度或不匹配 退出FOR
end;
end;
if (intJ=substringSize) then
begin
inti:=intk;
blnAdd:=true;
end;
end;
if (blnAdd=true) then
begin
ts.add(strstring);
strstring:='';
blnadd:=false;
end
else
//不进入匹配过程时做
begin
if blndoloop=true then
begin
curChar:=(pString+inti)^;
strstring:=strstring+curchar;
inti:=inti+1;
end;
end;
if inti>nsize-1 then
begin
ts.Add(strstring);
end;
blndoloop:=true;

end;
Result:=ts;
end;
 
我也來打擊一下大家的興趣。
關於Tstrings
這個規則與Tstrings的Qutoechar有關,它默認是雙引號,下面就以雙引號來說明。

當你的字符串中含有雙引號時小心了。
aaaa,"bb"ccc,dd
分為
aaaa
bb
ccc
dd
在分隔符後面碰到雙引號它會去找第二個雙引號,然後把其中的字符作為一個子串加入到列表中。
就說到這裡了,有興趣的去看一下TStrings的源碼。
 
to wac1104
沒空測試。
一進來就發現循環while intI<=(nSize-1) do,這樣substrig比較長時是不是會做一些無用功呀。
既然用指針運算了。還用strsearchSubStr:=strSearchSubStr+Curchar;這種語句干什麼。干脆全用指針算了。
程序有點亂,沒空細看。

字符串匹配很高深的學問。
 
我是用指针控制LOOP的应该不会有太多无用功,倒是
if ((PString+intI)^=pSubString^) then 后面的做了一些重复工作
因为后面的可以不必判断第一个字符。
另外DELPHI刚学,对于他的指针结构不是很清楚,只能用步长来控制指针了。
LICHAR您好像对DELPHI很熟悉,有空多指教。
 
我是指那個條件判斷。不用一直匹配到字符串結束。

DELPHI也是剛學,樓主才是大家伙嘍。呵呵。
 
if ((pString+intk)^=(PSubString+intj)^) and(intk<=nsize-1) then
指的是这句吗?拜托看明白再说!前面还一个FOR 那
之所以用(INTK〈=nisze-1))完全是为了容错!
要不然到了最后一个分割符后面的字符怎么办?
 
借樓主的寶地一用。
不是。是最前面那個while的條件。
100長的字符串裡找10長的子串,只要到91就OK了。
 
明白了!知道什么意思了,是有这个问题,当子字符串很长时,需要一个个的循环,做了很多无用功,谢谢!不过目前来看这个函数还算比较稳定,如果需要可以试用一下,况且谁又会将分割用的字符串设置的及其巨大 ……汗……,不会有这样的人吧?
另外如果可能可以再提出一些优化意见。谢谢!
 
新的SPLIT函数做了很多优化,去除了一些垃圾变量和垃圾代码。如果有什么不够完善的地方希望大家多给意见。
function SplitString(pString:Pchar;psubString:PChar):TStringList;
var
nSize,SubStringSize:DWord;
intI,intJ,intK:DWORD;
ts:TStringList;
curChar:Char;
strString:string;
strsearchSubStr:string;
begin
nSize:=strLen(pString);
SubStringSize:=strLen(PSubString);
ts:=TStringList.Create;
strstring:='';
inti:=0;
while intI<=(nSize-1) do
begin
if (nsize-inti)>= substringSize then
begin
if ((PString+intI)^=pSubString^) then
begin
intk:=inti;
strSearchSubStr:='';
curchar:=(pstring+intk)^;
strsearchSubStr:=strSearchSubStr+Curchar;
intk:=intk+1;
for intj:= 1 to SubStringSize-1 do
begin
if ((pString+intk)^=(PSubString+intj)^) then
begin
curchar:=(pstring+intk)^;
intk:=intk+1;
strsearchSubStr:=strSearchSubStr+Curchar;
end
else
begin
inti:=intk;
strString:=strString+strSearchSubStr;
break
//不匹配 退出FOR
end;
end;
if (intJ=substringSize) or (SubStringSize=1) then
begin
inti:=intk;
ts.add(strstring);
strstring:='';
end;
end
else
begin
curChar:=(pString+inti)^;
strstring:=strstring+curchar;
inti:=inti+1;
end;
if inti=nsize then
begin
ts.Add(strString);
strString:='';
end;
end
else
//将剩下的字符给作为一个字符串复制给字符串集合
begin
strString:=strstring+string(pString+inti);
ts.Add(strstring);
inti:=nsize;
end;
end;
Result:=ts;
end;
 
多人接受答案了。
 
后退
顶部