关于位操作的问题?(高难度)(100分)

  • 主题发起人 主题发起人 D_LOVER
  • 开始时间 开始时间
D

D_LOVER

Unregistered / Unconfirmed
GUEST, unregistred user!
如何从一个字节数组中提取连续的一串二进制数据
例如:
buf:array of byte;
如何从中提取任意起止位数的二进制数据?不一定正好从字节的边界处提取!
 
能对整个数组进行and操作吗?
我要提取的二进制位可能从一个字节中的某位开始,另一字节中的某位结束

这位仁兄能否给个示例呀
 
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
temp:array[1..8]of integer=(128,64,32,16,8,4,2,1);
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var buf:array [1..10] of byte;
buf_bin:string;
i,j:integer;
begin_pos,end_pos:integer
//起始位
begin
buf_bin:='';
for i:=1 to 10 do
buf:=i;
for i:=1 to 10 do //将所有数据转为二进制的字符串
for j:=1 to 8 do
if (bufand temp[j])=temp[j] then
buf_bin:=buf_bin+'1'
else
buf_bin:=buf_bin+'0';
edit1.Text :=buf_bin;
//根据起始位从处理出来的字符串中取出子字符串,此处代码不再赘述
end;

end.
有问题请上QQ:584740
 
var
FirstBit, Bits: Integer
// 初始位置(从0开始);要取的位数;
S: String
// 存放结果的串;
M, N, X, Y: Integer;
T: Byte;
begin
X:= Bits shr 3
if (Bits and 7) <> 0 then Inc(X);
SetLength(S, X);
Y:= FirstBit and 7;
if Y = 0 then
Move(Buf, S[1], X)
else begin
X:= FirstBit shr 3;
N:= 1;
try
repeat
T:= Buf[X] shl Y;
T:= T or (Buf[X+1] shr (8-Y));
S[N]:= chr(T);
Inc(N)
Inc(X);
until N > Length(S);
except end;
end

end;
 
看看这个类的帮助文件对你有帮助吗?TBits类,这是Delphi自带的
 
and 操作是最有效的。shr shl 差一点。
 
将字节数组转成字符串我也想到了,可是担心那样是否会影响效率?
TBits我也试了:
Move(ByteBuf,Bits[0],SizeOf(ByteBuf));
可是结果总是出錯,可能TBits不支持整块内存移动,只能一位一位的设置。

我的想法是尽量少用循环
 
小雨哥,能否给个用 and 实现的例子?
 
在你定义的数组里,从中提取任意起止位数的二进制数据,不一定正好从字节的边界处
提取,最有效的办法就是把它联成一片,你爱取哪里就取哪里,示例代码如下:
var
buf:array[0..7] of byte;
P1,T:Pointer;

设两个 TrackBar 条,一个初始 Position:=63
一个初始 Position:=0 总长度为 64 ,作为获取点的指示,
关键代码如下所示(不完整,流程实际通过):

GetMem(T,64);

for i:=0 to 7 do // 8*8=64 bit
begin
buf:=i*7
// 00;07;0E;15;1C;23;2A;31;随便置值
end;

P1:=@buf;
asm
PUSH EDI
PUSH ESI
PUSHF
MOV EDX,8
MOV ESI,P1
MOV EDI,T
@1: MOV AL,[ESI]
MOV [EDI],AL
INC ESI
INC EDI
SUB EDX,1
JNZ @1
POPF
POP ESI
POP EDI
end;

到这里,T 里已经将你数组里的数联成了一片,你根据
TrackBar 条的指示,爱怎么取怎么取,就行了。取出来
的数根据你的需要,想怎么转换那是你的事。
 
首先谢谢各位的作答;

对于小雨哥的例示,我有几个问题想问一下:
1。内存中的数组(buf 见上)与指针所指向的内存区它们的数据排列难道不是一样的吗,
 数组也占用的一块连续内存区,内存中的数据不都是用0和1表示的吗,这样看来,
 小雨哥所作的转换buf(数组)-> T(指针)就是不必要的了。
2。如果我上面的理解是错误的,那么请问小雨哥,我对T中的数据如何提取?
  是T..............还是能用 Move(T,...,10)等等之类的操作?
  在Delphi中操作的最小数据类型是字节类型?
3。可否把整块内存移入TBits中,如 Move(Buf,Bits[0],SizeOf(Buf))...........

以上问题只是我个人的理解
 
主要消除 Delphi 编译器违规警告。Array of Byte ,Tbits 都是预定义类型,我们知道
它们是 Flat Memory ,Delphi 却好象没有提供这样的例程,也就是说 Delphi 并不直接
支持在他们的任意 2 点之间&amp;不按字节对齐取值,况且我还不能确定 Compiler 最终会怎
么处理它们。虽然 T 也必须按计算机的要求取字节值,但是一个 AND EAX MASK 可以针
对字节进行掩码。类似于我们自己给自己提供了一个类型。(如果你直接可以从 Array 或
Tbits 中解决任意点开始取值当然最好,我是随手看随手写,只是提供一个思路,不是终
结方案)。我这样想也就这样写了,自己给自己搞了一个 Flat Memory 区,然后就可以直
接使用机器指令来工作,按 bit 或按 byte 或按 DB 、DD 取到合用的值,既快又安全。
你楼上的疑问我无法回答你,TBits 可以自动调节大小的特征就是你不能不做处理地直接
将一块你知道是相同大小的内存移入它内部。但使用 asm 处理应该可以,前提是你要获得
它的内存首地址。也就是说,这时候的 Delphi 已经不是你的帮手,具体工作由你自己分
管,应该是可以的吧。T 中取数,如果还是借助 Delphi 的例程,就又回去了,而且反而
难了。哎呀,写得太长了,看看上课时用的<汇编语言>,保证你有更好的办法的。
 
function ByteToBinary(b:byte):string;
var
i,n,m:integer;
begin
Result:='';
n:=b;
for i:=0 to 7 do
begin
m:=n mod 2;
n:=n shr 1;
if m=1 then result:='1'+result
else result:='0'+result;
end;
end;
function getBits(b:array of byte;nStart,nEnd:integer):string;
//开始、结束位置nStart,nEnd从0开始
var
n1,n2,i:integer;
begin
result:='';
if (nstart>nEnd) or (8*(high(b)+1)<nEnd-1) then exit;
n1:=nStart shr 3;
n2:=(nEnd+7) shr 3;
for i:=n1 to n2 do Result:=Result+ByteToBinary(b);
n1:=(nStart mod 8)+1;
n2:=nEnd-nStart+1;
Result:=copy(Result,n1,n2);
end;
procedure TForm1.Button5Click(Sender: TObject);
var
b:array[0..10] of byte;
i:integer;
begin
for i:=0 to 10 do b:=65+i;
showmessage(getbits(b,1,7));
showmessage(getbits(b,0,18));
showmessage(getbits(b,10,18));
end;
 
除了将字节数组转化为字符串提取外有没有更好的办法
 
这不难,也不用做循环:
Data: array of byte;

const buf:array[0..49]of byte
=($0,$0,$0,$0,$0,$1,
$0,$0,$0,$0,$0,$0,$20,$43,$4b,
$41,$41,$41,$41,$41,$41,$41,$41,
$41,$41,$41,$41,$41,$41,$41,$41,
$41,$41,$41,$41,$41,$41,$41,$41,
$41,$41,$41,$41,$41,$41,$0,$0,$21,$0,$1);

//注意ByteBeginNum,ByteEndNum从0到49。比特从0到7
procedure GetData(ByteBeginNum,ByteEndNum,BitBeginPos,BitEndPos: integer);
begin
SetLength(Data,ByteEndNum - ByteBeginNum+1);
data[0] := buf[ByteBeginNum] and ($FF shr BitBeginPos);
CopyMemory(@data[1],@buf[ByteBeginNum+1],ByteEndNum - ByteBeginNum-1);
data[ByteEndNum - ByteBeginNum] := buf[ByteEndNum] and ($FF shl BitEndPos);
end;
 
zw84611兄,如何将两端多余的0去掉呀,因为我要将提取出的转化为常见的数据类型
如 Integer
 
等待中。。。
 
后退
顶部