回sz_haitao,效果没有改善,我代码对吧? procedure TForm1.Button4Click(Sender: TObject); var s ,str:string; i :integer; const j = 10000; begin ProgressBar1.Max:=j; s := ''; str := ''; for i:=0 to j do begin if (i mod 1000) = 0 then begin str := str + s; s :=''; end else begin s := s + 'abcdefg'+'0x'+IntToHex(i,2); end; progressbar1.Position:= i; end;end;
我的程序是从bmp读数据转成c数组做的小工具, 读到bmp后就知道整个字符串长度了, 如果是 bgr :array of byte; setLength(bgr, size); 我知道怎么用,但字符串不知道了
procedure TForm1.Button4Click(Sender: TObject); var s ,str:string; i :integer; const j = 10000; begin ProgressBar1.Max:=j; s := ''; str := ''; for i:=0 to j do begin s := s + 'abcdefg'+'0x'+IntToHex(i,2); if (i mod 1000) = 0 then begin progressbar1.Position:= i; //移到这里! str := str + s; s :=''; end; end; if s<>'' then str:=str+s; end;
一样 setlength(str,n*(7+2+2)); //7:'abcdefg'+2:'0x'+2:IntToHex(i,2) for i:=... begin p:=(i-1)*11; str[p+1]:='a'; str[p+2]:='b'; ... end;
procedure TForm1.Button4Click(Sender: TObject); var a: array of char; L: integer; S: String; i: integer; const j = 10000; begin L:=0; Button4.Enabled := False; ProgressBar1.Max := j; for i := 0 to j do begin S:='abcdefg' + '0x' + IntToHex(i, 2); SetLength(a, L+Length(S)); for x:=1 to Length(S) do a[L+x-1]:=S[x]; L:=L+Length(S); ProgressBar1.Position := i; Application.ProcessMessages; end; SetLength(a, L+1); a[L]:=#0; S:=String(A); Button4.Enabled := True; end; 上面是我参考StringBuilder的概念随手写的代码, 没有Debug,肯定有很多错,只是用以表达我的想法, 说明“连续内存把所有字符串都存好最后一次取用”的概念。觉得你还是直接把TStringBuilder代码取来用比较简单。
procedure TForm1.Button4Click(Sender: TObject); var i: Integer; ss: TStringStream; s: string; const j = 10000;begin ss := TStringStream.Create(''); try ProgressBar1.Max:=j; for i := 0 to j do begin ss.WriteString('abcdefg0x' + IntToHex(i,2)); progressbar1.Position:= i; end; s := ss.DataString; finally ss.Free; end; end;使用TStringStream试试。
//处理bmp数据到bgr数组 bmp_height := bmp.Height; bmp_width := bmp.Width; sum :=0;//总像素点数 j:=1; ProgressBar1.Max:=ms.Size; for i:=0 to bmp_height -1 do //多少行 begin P := bmp.scanline[i]; //取行首地址指针 for m:=0 to (bmp_width * 3)-1 do //每行有多少点 ,24位色是3个字节一个像素。 begin //用每行的点*3正好解决bmp数据存储4字节对齐问题 if ((j mod 16) = 0) then //每行16字节 begin s := '0x'+ IntToHex(P[m],2)+','+#13#10; //添加回车换行 end else begin s := '0x'+ IntToHex(P[m],2)+','; end; // ss.WriteString(s); //注释掉需要24秒,不注释61秒, //这个方法不如frank_lee_cn 的用a: array of char;方法 //但我的程序IntToHex(P[m],2)估计废了不少时间,如果不用到这个转换函数,有什么方法把bmp的 //数据转成C数组0x00这个样子? progressbar1.Position:= j; j :=j+1; end;// for m:=0 end; //for i:=0
for i:=1 to j do
StringBuilder.Append('abcdefg'+'0x'+IntToHex(i,2));result:=StringBuilder.ToString;它的概念很简单,就是连续内存把所有字符串都存好,
暂时不取用,直到取用时一次性赋值给String.
我没测过,但从理论上来说应该是能解决你的问题。至于Delphi从哪一个版本开始提供StringBuilder,
那我就不知道了,
你自己看看你用的版本有没有啰。依我的想法,
就算你用的版本没有支持,
去有支持的版本copy source过来,
估计也可以。
var
s: string;
i: integer;
const
j = 10000;
begin
Button4.Enabled := False;
ProgressBar1.Max := j;
for i := 0 to j do
begin
s := s + 'abcdefg' + '0x' + IntToHex(i, 2);
ProgressBar1.Position := i;
Application.ProcessMessages;
end;
Button4.Enabled := True;
end;
版本是delphi7,没有StringBuilder 类,我按“F1”没有帮助提示。
你的思路是对的,
我想过用字符数组来,每个字符长度是一定的就是0x00,不知道该怎么写数组,请教了。
procedure TForm1.Button4Click(Sender: TObject);
var
s ,str:string;
i :integer;
const
j = 10000;
begin
ProgressBar1.Max:=j;
s := '';
str := '';
for i:=0 to j do
begin
if (i mod 1000) = 0 then
begin
str := str + s;
s :='';
end
else
begin
s := s + 'abcdefg'+'0x'+IntToHex(i,2);
end; progressbar1.Position:= i;
end;end;
读到bmp后就知道整个字符串长度了,
如果是
bgr :array of byte;
setLength(bgr, size);
我知道怎么用,但字符串不知道了
procedure TForm1.Button4Click(Sender: TObject);
var
s ,str:string;
i :integer;
const
j = 10000;
begin
ProgressBar1.Max:=j;
s := '';
str := '';
for i:=0 to j do
begin
s := s + 'abcdefg'+'0x'+IntToHex(i,2);
if (i mod 1000) = 0 then
begin
progressbar1.Position:= i; //移到这里!
str := str + s;
s :='';
end;
end;
if s<>'' then
str:=str+s;
end;
一样
setlength(str,n*(7+2+2)); //7:'abcdefg'+2:'0x'+2:IntToHex(i,2)
for i:=...
begin
p:=(i-1)*11;
str[p+1]:='a';
str[p+2]:='b';
...
end;
procedure TForm1.Button4Click(Sender: TObject);
var
a: array of char;
L: integer;
S: String;
i: integer;
const
j = 10000;
begin
L:=0;
Button4.Enabled := False;
ProgressBar1.Max := j;
for i := 0 to j do
begin
S:='abcdefg' + '0x' + IntToHex(i, 2);
SetLength(a, L+Length(S));
for x:=1 to Length(S) do
a[L+x-1]:=S[x];
L:=L+Length(S);
ProgressBar1.Position := i;
Application.ProcessMessages;
end;
SetLength(a, L+1);
a[L]:=#0;
S:=String(A);
Button4.Enabled := True;
end;
上面是我参考StringBuilder的概念随手写的代码,
没有Debug,肯定有很多错,只是用以表达我的想法,
说明“连续内存把所有字符串都存好最后一次取用”的概念。觉得你还是直接把TStringBuilder代码取来用比较简单。
所以Delphi的代码通常不会一次只增加所需内存,
而是需要时增加一块大一点的内存供使用,
以此来减少SetLength执行次数。我上面随手写的代码不包含这个机制,
但Delphi的代码里是有的,
因此我一直强调直接用TStringBuilder更好。
progressbar1.Position:= i; //移到这里!
不能移到mod里面,本来就是为了测试速度的。
能否帮我提取下TStringBuilder
11楼你写的代码,没改善。也是一开始快,到后面慢。
我的测试方法有问题,刚才仔细看了你的代码,你是用数组方式来实现的,每次放进去的只是当前的字符,
需要累加。我实际情况是每次字符串长度是固定的如:‘0x00,’共5个长度。不需要每次都setlength.
接下来我按你的方法放到实际程序中,让它处理大bmp图片试试。
稍后汇报。
按你方法测试
处理一个234*198的bmp图片,生成约680 000个数据需要30秒。按我自己原先方法是需要71秒;速度有提高,但还不够快,等半分钟是难易忍受的。有没有更好的办法?
var
i: Integer;
ss: TStringStream;
s: string;
const
j = 10000;begin
ss := TStringStream.Create('');
try
ProgressBar1.Max:=j;
for i := 0 to j do
begin
ss.WriteString('abcdefg0x' + IntToHex(i,2));
progressbar1.Position:= i;
end;
s := ss.DataString;
finally
ss.Free;
end;
end;使用TStringStream试试。
只用一次SetLength
其后都是填数据的过程。
想想有没有更好的方式。
var
S , sBuf : String;
i , iUsed , iLen , iMax : integer;
const
j = 1000000; //扩大了100倍
begin
ProgressBar1.Max := j;
iUsed := 0;
iMax := 0;
for i:=0 to j do begin
S := 'abcdefg'+'0x'+IntToHex(i,2);
iLen := Length(S);
if iLen+iUsed>iMax then begin
inc(iMax , 1024*1024);
SetLength(sBuf , iMax);
end;
{$IFDEF CPUX64}
Move(Pointer(S)^ , Pointer(Int64(sBuf)+iUsed)^ , iLen * SizeOf(Char));
{$ELSE}
Move(Pointer(S)^ , Pointer(Integer(sBuf)+iUsed)^ , iLen * SizeOf(Char));
{$ENDIF}
inc(iUsed , iLen);
end;
Delete(sBuf , iUsed+1 , iMax);
//到此,sBuf就是累加的结果了
//这句移动到最后了,因为整个过程不到1秒,放到中间反而慢得很
progressbar1.Position := progressbar1.Max;
end;
ms:=TMemoryStream.Create;
bmp :=TBitMap.Create;
ms.Clear;
s := OpenDialog2.FileName;
ms.LoadFromFile(s); //装载对应路径下的图片
ms.Position:=0;
bmp.LoadFromFile(s);//装载对应路径下的图片
//处理bmp数据到bgr数组
bmp_height := bmp.Height;
bmp_width := bmp.Width;
sum :=0;//总像素点数
j:=1;
ProgressBar1.Max:=ms.Size;
for i:=0 to bmp_height -1 do //多少行
begin
P := bmp.scanline[i]; //取行首地址指针
for m:=0 to (bmp_width * 3)-1 do //每行有多少点 ,24位色是3个字节一个像素。
begin //用每行的点*3正好解决bmp数据存储4字节对齐问题
if ((j mod 16) = 0) then //每行16字节
begin
s := '0x'+ IntToHex(P[m],2)+','+#13#10; //添加回车换行
end
else
begin
s := '0x'+ IntToHex(P[m],2)+','; end;
// ss.WriteString(s); //注释掉需要24秒,不注释61秒,
//这个方法不如frank_lee_cn 的用a: array of char;方法
//但我的程序IntToHex(P[m],2)估计废了不少时间,如果不用到这个转换函数,有什么方法把bmp的
//数据转成C数组0x00这个样子? progressbar1.Position:= j;
j :=j+1;
end;// for m:=0
end; //for i:=0
我前面的测试都是错误了,
发现progressbar1.Position:= j; 这个才是耗时的罪魁祸首。
我接下来去掉进度条重新测试一遍各位提供的方法。
被你这一步弄坏了S:=String(A);,还有方法么?
看:
s := '0x'+ IntToHex(P[m],2)+','+#13#10; //添加回车换行
SetLength(a, L+1);
a[L]:=#0;
S:=String(A);
所以要
if j mod 1000=0 then
progressbar1.Position:= j;
的ss := TStringStream.Create('');方法去掉后
进度条测试转换图片数据需要46秒,但转换出来的数据是符合我要求的。
所以要
if j mod 1000=0 then
progressbar1.Position:= j;
你说的有道理,昨天没有理解你的意思,现在想想你说的对。
因为出现了好几个0,
感觉比较可能是下面这二行的错。
for x:=1 to Length(S) do
a[L+x-1]:=S[x];
昨天写的时候告诉你我没Debug了,(只是随手在网上写的,没有运行IDE)
你应该自己设法debug才是。我通常在论坛回复,
不会给那么精准的答案,
只是表达意思而已,
Debug得你自己多伤脑筋才是。另,27楼好像给了你很棒的答案,
何不参考?
与frank_lee_cn 代码对比的话,两种思路是一个是指针,一个是数组,字符需要不停累加,只追加在字符串尾。在C语言里从根本上来讲数组和指针是差不多的,不敢说一样,记得网上有大神专门反驳过。
本例程序还是指针方式好,对原数据没有进行转变,只是搬运。
结贴,谢谢各位热心帮忙,只有20分平分给大家了,分数只是小事,让大家看到你们的热情才是值得发扬的。