AnsiPos,ByteType,AnsiStrScan 等函数对非当前代码页不支持 ( 积分: 200 )

  • 主题发起人 主题发起人 xuechao
  • 开始时间 开始时间
无语,我已经说过几次了,为什么你都不肯试试Pos函数呢?我很怀疑你是否认真看了别人的回答,那你提问还有什么意思啊?
最后再说一次:
1. 你要做的仅仅是查找和替换,查找过程中没有必要知道每个字符是单字节字符还是多字节字符。所以根本不需要用到AnsiPos、ByteType和AnsiStrScan等与特定字符集相关的函数。
2. Pos函数虽然不能在包含多字节字符的字符串中按照字符来逐个匹配,但根据多字节字符的组成原理:当两个多字节字符中的每个组成字节都分别相同时,我们理所当然地可以认为这两个字符就是同一个字符。这样,不论你要查找的或被查找的字符串是否包含有多字节字符,用Pos函数来找都绝对不可能会出错。

finally,如果你确实很有空,想自己写一个类似Pos的函数,并且可以在任何语言环境下运行。那么OK,这里有一个基本的框架,有什么特别的需要你自己加上去吧:
function StrPos(SubS,S:string):integer;
var
i,Posi,StrLen,SubStrLen:integer

begin
if (SubS<>'') and (S<>'') then
begin
i:=1;
StrLen:=Length(S);
SubStrLen:=Length(SubS);
Posi:=0;
While (i<=StrLen) and (Posi<1) do
if Copy(S,i,SubStrLen)=SubS then
Posi:=i
else
inc(i);
Result:=Posi
end
else
Result:=0
end;
 
SparkV 兄弟真是热心人,佩服,学习
 
to SparkV:
我明白你的意思,但POS不符合multi byte character set的要求。比如:
Pos('卫','何丽'), “何”字的第二字节和“丽”字的第一字节刚好组成“卫”字,
所以结果为2。这不是我想要的结果。
又:pos('|','億'),“億”字的第二字节是#$7C,刚好是“|”字符的 ASCII码,
所以结果为2,这不是我想要的结果。
Pos 用在 multi byte character set 中有问题,这不用再试了。
不知你是否明白我的意思?
 
还有一种方法,利用一下中文字符串的特征:2字节表示一个中文字符,即搜索时遇到中文字符则开始计数,到达找到字符串处判断一下此前连续的中文字符之字节数(注意是连续),如果此前连续的中文字节数为偶数,则找到;否则未找到。
 
to dreamisx:
ByteType 函数就是做这个的工作的。

ByteType 函数的改进
Windows平台
利用DBCS字符集中字符只有一个字节或两个字节的特点,从当前字节的前一个字节从后往前分析,这个方法很好。
其实可以优化一下,当前字节不是DBCS字符集的第一字节的范围,也不是DBCS字符集的第二字节的范围,结果就是mbSingleByte。
将 in LeadBytes 的判断改用 IsDBCSLeadByteEx API 判断就可以适应不同的字符集了。
BOOL IsDBCSLeadByteEx(
UINT CodePage, // identifier of code page
BYTE TestChar // character to test
);
这种方法不适用于Windows平台的 GB18030字符集
GB18030 的双字节汉字
第一字节:0x81-0xFE 第二字节:0x40-0x7E, 0x80-0xFE
GB18030 的四字节汉字
第一字节:0x81-0xFE 第二字节:0x30-0x39 第三字节:0x81-0xFE 第四字节:0x30-0x39

LINUX平台
因为一个字符由一到六个字节组成,只有从字符串的开始从前往后进行分析。

function ByteType(const S: string
Index: Integer): TMbcsByteType;
begin
Result := mbSingleByte;
if SysLocale.FarEast then
Result := ByteTypeTest(PChar(S), Index-1);
end;

//Windows 平台代码
function ByteTypeTest(P: PChar
Index: Integer): TMbcsByteType;
var
I: Integer;
begin
Result := mbSingleByte;
if (P = nil) or (P[Index] = #$0) then Exit;
if (Index = 0) then
begin
if P[0] in LeadBytes then Result := mbLeadByte;
end
else
begin
I := Index - 1;
while (I >= 0) and (P in LeadBytes) do Dec(I);
if ((Index - I) mod 2) = 0 then Result := mbTrailByte
else if P[Index] in LeadBytes then Result := mbLeadByte;
end;
end;

//LINUX 平台代码
function ByteTypeTest(P: PChar
Index: Integer): TMbcsByteType;
var
I, L: Integer;
begin
Result := mbSingleByte;
if (P = nil) or (P[Index] = #$0) then Exit;

I := 0;
repeat
if P in LeadBytes then
L := StrCharLength(P + I)
else
L := 1;
Inc(I, L);
until (I > Index);

if (L <> 1) then
if (I - L = Index) then
Result := mbLeadByte
else
Result := mbTrailByte;
end;
 
呵呵,不好意思,是我考虑不周。的确应该判断每个字符的类型。其实问题就集中在ByteType函数上,单就你的问题来说,可以专门写一个基于GBK字符集的GBKByteType函数:在这个函数里面,用专用于GBK字符集的GBKLeadBytes 变量来取代原来的LeadBytes 变量。其实所谓的LeadBytes 其实就是一个包含了系统默认语言字符集中,第一个字节所有可能的取值。因此,当我们初始化我们自己的专用于GBK字符集的GBKLeadBytes 变量的时候,可以将GBK编码标准中,第一个字节编码范围内的所有取值加入这个GBKLeadByte 集合即可。

下面这个是我刚写的一个程序,里面就自己重写了ByteType和Pos函数(分别命名为GBKByteType和GBKPos),其实也很简单,只要把里面对LeadBytes变量的引用改成对GBKLeadByte变量的引用就可以了。看看是不是能对你的程序有所帮助?


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;

type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Button1: TButton;
RadioGroup1: TRadioGroup;
Button2: TButton;
Button3: TButton;
Label1: TLabel;
Label2: TLabel;
procedure RadioGroup1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

GBKLeadBytes:set of char;

function GBKByteTypeTest(P:PChar;Index:Integer):integer;
function GBKByteType(S:string;Index:integer):integer;
function GBKPos(SubStr,S:string):integer;

implementation

{$R *.dfm}

function GBKByteTypeTest(P:PChar;Index:Integer):integer;
var
I: Integer;
begin
Result := 0;
if (P = nil) or (P[Index] = #$0) then Exit;
if (Index = 0) then
begin
if P[0] in GBKLeadBytes then Result := 1;
end
else
begin
I := Index - 1;
while (I >= 0) and (P in GBKLeadBytes) do Dec(I);
if ((Index - I) mod 2) = 0 then Result := 2
else if P[Index] in GBKLeadBytes then Result := 1;
end;
end;

function GBKByteType(S:string;Index:integer):integer;
begin
Result:=0;
if SysLocale.FarEast then
Result:=GBKByteTypeTest(PChar(S), Index-1);
end;

function GBKPos(SubStr,S:string):integer;
var
i,j,Posi,StrLen,SubStrLen:integer;
begin
if (SubStr<>'') and (S<>'') then
begin
StrLen:=Length(S);
SubStrLen:=Length(SubStr);
Posi:=0;
i:=1;
While (i+SubStrLen-1<=StrLen) and (Posi<1) do
begin
if (GBKByteType(S,i)=GBKByteType(SubStr,1))
and
(S=SubStr[1])
then
begin
Posi:=i;
j:=2;
inc(i);
While (j<=SubStrLen)
and
(GBKByteType(S,i)=GBKByteType(SubStr,j))
and
(S=SubStr[j])
do
begin
inc(i);
inc(j)
end;
if j<=SubStrLen then
Posi:=0
end
else
inc(i)
end;
Result:=Posi
end
else
Result:=0
end;

procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
case RadioGroup1.ItemIndex of
0:
begin
Edit1.Text:='|';
Edit2.Text:='億'
end;
1:
begin
Edit1.Text:='卫';
Edit2.Text:='何丽'
end
end
end;

procedure TForm1.FormShow(Sender: TObject);
begin
RadioGroup1.OnClick(Application)
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
showmessage(inttostr(Pos(Edit1.Text,Edit2.Text)))
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
showmessage(inttostr(AnsiPos(Edit1.Text,Edit2.Text)))
end;

procedure TForm1.FormCreate(Sender: TObject);
var
GBKLeadByte:char;
begin
GBKLeadBytes:=[];
for GBKLeadByte:=Chr($81) to Chr($FE) do
Include(GBKLeadBytes,GBKLeadByte)
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
showmessage(inttostr(GBKPos(Edit1.Text,Edit2.Text)))
end;

end.
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
后退
顶部