有谁成功地使用过StrLower这个函数吗?(100分)

  • 主题发起人 guest2000
  • 开始时间
G

guest2000

Unregistered / Unconfirmed
GUEST, unregistred user!
那只不过是PCHAR向string做了个隐含转换罢了吧
这种事当然轮不到我来钻牛角尖,要钻也要宝兰的开发人员钻。唉 , poor delphi
 
G

guest2000

Unregistered / Unconfirmed
GUEST, unregistred user!
不过StrCopy的例子还表现正常:
var
Buffer: PChar;
begin
GetMem(Buffer,Length(Label1.Caption) + Length(Edit1.Text) + 1);
StrCopy(Buffer, PChar(Label1.Caption));
StrCat(Buffer, PChar(Edit1.Text));
Label1.Caption := Buffer;
Edit1.Clear;
FreeMem(Buffer);
end;
function StrCopy(Dest: PChar
const Source: PChar): PChar;
asm
PUSH EDI
PUSH ESI
MOV ESI,EAX
MOV EDI,EDX
MOV ECX,0FFFFFFFFH
XOR AL,AL
REPNE SCASB
NOT ECX
MOV EDI,ESI
MOV ESI,EDX
MOV EDX,ECX
MOV EAX,EDI
SHR ECX,2
REP MOVSD
MOV ECX,EDX
AND ECX,3
REP MOVSB
POP ESI
POP EDI
end;

function StrLower(Str: PChar): PChar
assembler;
asm
PUSH ESI
MOV ESI,Str
MOV EDX,Str
@@1: LODSB
OR AL,AL
JE @@2
CMP AL,'A'
JB @@1
CMP AL,'Z'
JA @@1
ADD AL,20H
MOV [ESI-1],AL
JMP @@1
@@2: XCHG EAX,EDX
POP ESI
end;
 
G

guest2000

Unregistered / Unconfirmed
GUEST, unregistred user!

独帅

Unregistered / Unconfirmed
GUEST, unregistred user!
原因可能是在进行类型转换的时候有错误(BUG?不知道),
但是如果这样写就没有问题了
procedure TForm1.Button1Click(Sender: TObject);
var
s1: String;
begin
s1 := 'asdmfnbSDAKFHASDKJFxc,mvnkdj';
//StrLower(PChar(s1));//这样会有错误
StrLower(@s1[1])
//没有错误
ShowMessage(s1)
//结果全是小写的
end;
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
我回答这句:
>> 原因可能是在进行类型转换的时候有错误.....
差不多吧。真正的原因是 does not perform any length checking ,
意思大概是“在转换中不做长度检查”吧。如果这个分析正确,那么我们
给它一个保证可以不出现长度违规的例程的话,应该可以通过,一起来试试。
例子还是 独帅 的那个,我们给它分配内存,情况大概如下:
var
s1: String;
p1:pChar;
begin
s1 := 'asdmfnbSDAKFHASDKJFxc,mvnkdj';
GetMem(p1, StrLen(PChar(s1)));
StrPCopy(p1,s1);
StrLower(p1);
// StrLower(PChar(s1));// 这样会有错误
// StrLower(@s1[1])
// 没有错误
ShowMessage(p1)
// 结果全是小写的
FreeMem(p1);
end;
从中我们可以明确地看到,我们给 PChar 分配了内存,然后才使用
StrLower 函数转换大小写。执行的结果.....一切正常。
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
怎么没人发言了?
 
C

chuguozhen

Unregistered / Unconfirmed
GUEST, unregistred user!
const

S: PChar = 'A fUnNy StRiNg'//错在这里,PChar是指针,必须先分配内存
begin
Canvas.TextOut(5, 10, string(StrLower(S)) + ' ' + string(StrUpper(S)));
end;
 
G

guest2000

Unregistered / Unconfirmed
GUEST, unregistred user!
to 小雨哥:
看来要等熟汇编的来才搞得清
to chuguozhen:
必须先分配内存,搞错了吧!
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
TO:guest2000
有何高见?
 
J

jsxjd

Unregistered / Unconfirmed
GUEST, unregistred user!
这个函数的汇缟代码在 d5 和 d6 中是完全一样的。
但 D6 中没有任何问题。
D5 中这行通不过: MOV [ESI-1],AL

以为DCU文件有问题,可是我删除了所有的 sysUtils.dcu 。
程序还能编译运行,而且没有生成新的 sysUtils.dcu。当然
原来的问题还是存在。这是什么原因????
哪些DCU是真正需要的????
如何生成新的 sysUtils.dcu?????
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
TO:jsxjd ,你好:
我只装了 D5,D7,看 D7 同一函数在 sysUtils.pas 中的代码和 D5 也是一样。
于是我运行:
const
S: PChar = 'A fUnNy StRiNg'
begin
Canvas.TextOut(5, 10, string(StrLower(S)) + ' ' + string(StrUpper(S)));
end;
结果错误。于是又运行:
procedure TForm1.Button1Click(Sender: TObject);
var
s1: String;
begin
s1 := 'asdmfnbSDAKFHASDKJFxc,mvnkdj';
StrLower(PChar(s1));//这样会有错误
// StrLower(@s1[1])
//没有错误
ShowMessage(s1)
//结果全是小写的
end;
这段也错误。这段代码上面我引用过,所以特意重新看了看,情况是:
字符为小写时没有异常,当执行到 asdmfnbS 的 S 时,ESI 的指针依代码所示减一,
因 LODSB 时,该指针已经指向下一个字符,减一应该是正确的,但调试代码跳转到
函数 _HandleFinally 处(在 System.pas 内)。这时 EAX 被置入 [ESP+4], 样子是
准备退出了,呵呵,什么魔法哩?不懂。
执行我上面 ID:1392986 的代码时,顺利执行,又为什么?为什么啊?
 
J

jsxjd

Unregistered / Unconfirmed
GUEST, unregistred user!
我查看了汇缟代码,这是因为没有大写时,这一行“MOV [ESI-1],AL”永远不会执行!!
可能和参数传递的地址有关系。
或者某个DCU包有问题。
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
首先感谢 kcahcn 的提醒。
Delphi 没有错,虽然他没有明确告诉我 why ,但可以肯定是长度计算上的问题,
是 Pascal 编译器中的特色。Delphi 帮助上有这段话:
If the source string contains international characters, use AnsiStrLower
instead. If the source string is a Pascal string, use LowerCase (or
AnsiLowerCase) instead.
意思大概是说:如果字符中有“international characters”,就要用 AnsiStrLower 来
代替 StrLower (我用它代入使用,结果一样失败),如果是“Pascal string”就用
LowerCase (or AnsiLowerCase) 代替(我用它代入使用,结果成功)。
其中 AnsiStrLower 是利用 Windows 的 function CharLower
external user32 name 'CharLowerA';
而 LowerCase (or AnsiLowerCase) 就是增加了 SetLength(s1,StrLen(s1));来分配内存。
类似于将程序改为了(代码成功通过):
procedure TForm1.Button1Click(Sender: TObject);
var
s1:String;
begin
s1 := 'asdmfnbSDAKFHASDKJFxc,mvnkdj'

SetLength(s1,255)
// 故意使用 255 的 ,代码简单点
StrLower(PChar(s1));// 这样已经没有错误了
ShowMessage(s1)
// 结果全是小写的
end

但还是有一个有趣的问题,现在我要出去办事,回来再说。
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
上面说到“Pascal 编译器中的特色”,只是为了说得好听点,应该怎么说?
看下面的定义:
var
s1:string
// 这里可以是 String 或 AsinString 或 WideString
begin
s1:='ABCD....xyz'
// 这里给它放 > 256 个字符。
end;
编译器会抱怨给 s1 赋的字符多于 255 个。
但是这样就可以:
begin
s1:='ABCD...xyz'{共 200 个} + 'ABCD...xyz'{共 200 个}
// 这里实际是 400 个
end;
编译器可以通过。
许多资料上显示 String 是动态字符串,长度可以很大。但实际上 String 的结构限制
了一次表达的数量,它的长度位初始化只是提供了一个字节(8 Bit)。为了得到 s1 的实
际数量,编译器需要反复调用长度计算例程,如果代码中没有让它反复调用计算,它就限
制你一次调用的长度不大于 255 。这也是早期 ShortString 的影子。即便在使用 s1 前
我们给它一个大内存也是这样:
begin
SetLength(s1,500);
s1:='ABCD....xyz'
// 这里给它放 > 256 个字符。
end;
编译器还是不能通过。这就是 Pascal 编译器的特色 。
 
J

jsxjd

Unregistered / Unconfirmed
GUEST, unregistred user!
从汇缟代码看,串必须是 0 终止的.
D6为什么没有问题,难道默认设置的问题.
 
G

guest2000

Unregistered / Unconfirmed
GUEST, unregistred user!
一起加油
 
C

chuguozhen

Unregistered / Unconfirmed
GUEST, unregistred user!
Pchar 类型就是得先分配内存,错在那里,说出来,好让大家明白
 
D

DreamTiger

Unregistered / Unconfirmed
GUEST, unregistred user!
这是StrUpper的源码,可以看到,和StrLower基本上是一模一样的。

问题出在这里:
function StrUpper(Str: PChar): PChar
assembler;
asm
PUSH ESI
MOV ESI,Str //参数字符串作为源地址
MOV EDX,Str //同样,把参数字符串作为目标地址
@@1: LODSB
OR AL,AL
JE @@2
CMP AL,'a'
JB @@1
CMP AL,'z'
JA @@1
SUB AL,20H //目标字符串的字符转为大写
MOV [ESI-1],AL
JMP @@1
@@2: XCHG EAX,EDX
POP ESI
end;

由此我们可以看到,输入的字符串同时作为输出的字符串地址,
这就要求我们输入的字符串必须能够修改。

看看例子:
uses SysUtils;
var
S: PChar;
begin
S:= 'A fUnNy StRiNg';//把一个常量字符串的地址赋给S
S := StrLower(S);//对该常量字符串进行StrLower操作。当然出错。
end;

看下面的例子:
procedure TForm1.Button2Click(Sender: TObject);
var
MyArray: array[0..32] of Char;
S: PChar;
begin
MyArray := 'A fUnNy StRiNg';
S := MyArray;
S := StrUpper(S);
ShowMessage(string(MyArray));
end;
通过,显示为全大写的A FUNNY STRING

分析过程结束,呵呵。
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
// 不考虑字符的其他尺寸,只考虑 ASCII 尺寸
// StrLower 测试代码简写为以下状况
asm
PUSH ESI // 入栈保护
MOV ESI,Str // 字符串起始地址
MOV EDX,Str // 保存字符串起始地址
@1: MOV AL,[ESI] // ESI 指向的内容送入 AL
INC ESI // ESI 值加一
OR AL,AL // 是 0 吗?
JE @2 // 是,转 @2
CMP AL,'A' // 比 'A' 小吗?
JB @1 // 是,转 @1
CMP AL,'Z' // 比 'Z' 大吗?
JA @1 // 是,转 @1
ADD AL,20H // AL 内容加 20H
MOV [ESI-1],AL // AL 值送入 ESI-1 指向的单元
JMP @1 // 转 @1
@2: XCHG EAX,EDX // 返回字符串首地址
POP ESI // 恢复
end;
奇怪的是,为什么我不能对在控制范围内的地址进行操作?假如我干脆将测试代码写到
自己的程序里,并且定死字符串长度,就可以不使用 EDX 单元,如下:
s1:='asdmfnbSDAKFHASDKJFxc,mvnkdj'
// 这个串长度是 1CH
这时的 ASM 改为如下:
asm
PUSH ESI // 入栈保护
MOV ESI,Str // 字符串起始地址
@1: MOV AL,[ESI] // ESI 指向的内容送入 AL
INC ESI // ESI 值加一
OR AL,AL // 是 0 吗?
JE @2 // 是,转 @2
CMP AL,'A' // 比 'A' 小吗?
JB @1 // 是,转 @1
CMP AL,'Z' // 比 'Z' 大吗?
JA @1 // 是,转 @1
ADD AL,20H // AL 内容加 20H
MOV [ESI-1],AL // AL 值送入 ESI-1 指向的单元
JMP @1 // 转 @1
@2: MOV EAX,ESI-1CH // 返回字符串首地址
POP ESI // 恢复
end;
运行后错误依然在{ MOV [ESI-1],AL }这句。是谁在保护?怎么保护?
如果错误这么明显,Delphi 设计者在新发行的 Delphi 7 中居然还保留它?
 

Similar threads

I
回复
0
查看
629
import
I
I
回复
0
查看
578
import
I
I
回复
0
查看
1K
import
I
I
回复
0
查看
3K
import
I
顶部