I have simple question here. How to convert WideChar to 2xByte in Delphi - 7? I searched the internet and the StackOverflow but with no results...
David gave you the preferable way, namely,
var
b1, b2: Byte;
wc: WideChar;
...
b1 := WordRec(wc).Lo;
b2 := WordRec(wc).Hi;
A few other options (just for fun):
b1 := Lo(Word(wc));
b2 := Hi(Word(wc));
and
b1 := Byte(wc);
b2 := Byte(Word(wc) shr 8);
and
b1 := PByte(#wc)^;
b2 := PByte(NativeUInt(#wc) + 1)^;
and
var
wc: WideChar;
bytes: WordRec absolute wc;
begin
// Magic! The bytes are already found in bytes.Lo and bytes.Hi!
Lots of ways to do this. For example my personal choice would be:
var
b1, b2: Byte;
wc: WideChar;
....
b1 := WordRec(wc).Lo;
b2 := WordRec(wc).Hi;
Related
I have a device that sends couple bytes of info about its status, and the first 5 bytes are statuses of 40 pushbuttons. Each bit represents one button, being 1 for released and 0 for pressed.
From that matter, I was doing a checkout using this piece of code:
procedure TForm2.ServerUDPRead(AThread: TIdUDPListenerThread;
AData: array of Byte; ABinding: TIdSocketHandle);
var received: array [0..4] of string; a1, a2 integer;
bits: array [0..40] of boolean;
begin
for a1 := 0 to 4 do
Received[a1] := (ByteToBin(AData[a1]));
for a1 := 4 downto 0 do
begin
for a2 := 1 to 8 do
begin
if Copy(Received[a1], a2, 1) = '0' then bits[(4-a1)*8+(a2-1)]:=False else bits[(4-a1)*8+(a2-1)]:=True;
end;
end;
ByteToBin being function for coverting byte to string of 8 bits...
Is there an alternate "easier", simplified and (what I'm actually interested in) faster procedure for achieving what I need, or is this way good enough?
I find it a bit sluggish and dirty... :/ (although ultimately, does it's intention... so interested mostly out of learning point of view)
With this you an convert directly from AData to array of boolean:
for i := 0 to 39 do
bits[i] := (AData[i div 8] and (1 shl (i mod 8))) <> 0;
I'm not sure if the order of the booleans is exactly the same, I lost count with all your indexes :)
An alternative approach is using a set:
type
TByteSet = set of 0..7;
...
for I := 0 to 39 do begin
bits[I] := (I mod 8) in TByteSet(AData[I div 8]);
end;
I have 10 double variables I would like to initialize with the value 0. They are unstructured and not part of an array by design.
procedure Initialize;
var
a1, a2, a3, a4, a5, b1, b2, b3, b4, b5: Double;
begin
a1 := 0;
a2 := 0;
a3 := 0;
a4 := 0;
a5 := 0;
b1 := 0;
b2 := 0;
b3 := 0;
b4 := 0;
b5 := 0;
end;
To refactor that piece of code, I'm introducing a helper method AssignValue.
procedure Initialize;
var
a1, a2, a3, a4, a5, b1, b2, b3, b4, b5: Double;
begin
AssignValue(0,a1);
AssignValue(0,a2);
...
end;
procedure AssignValue(value: Double; var target: Double);
begin
target:= value;
end;
How do I write a more general AssignValue procedure that takes an arbitrary number of arguments and make the call AssignValue(0,a1,a2,a3,a4,a5,b1,b2,b3,b4,b5) possible?
Bonus question: How do you write that procedure so that it takes into account double or int reference in any order, assuming value: Int as first parameter.
You could do it like this:
procedure AssignValue(const Value: Double; const Addresses: array of PDouble);
var
i: Integer;
begin
for i := low(Addresses) to high(Addresses) do
Addresses[i]^ := Value;
end;
Call it like this:
AssignValue(0.0, [#a1, #a2, #a3, ...]);
Here we are passing an open array containing the addresses of your variables.
To support multiple types you would use overloads declared like this:
procedure AssignValue(const Value: Double; const Addresses: array of PDouble);
overload;
procedure AssignValue(const Value: Integer; const Addresses: array of PInteger);
overload;
// and so on, implementation of these functions is obvious
It's up to you to judge whether or not this is any better than your current solution. Personally, I'd stick with the plain old assignment operator. Another option would be to put the variables inside a record and assign Default(TMyRecord) to your record variable.
You can use open array parameters for this:
procedure AssignValue(value: double; const arr: array of PDouble);
var
i: Integer;
begin
for i := 0 to length(arr)-1 do
PDouble(arr[i])^ := value;
end;
use it like this (i don't see the way to avoid of "#" for such task):
AssignValue(1, [#a1,#a2,#a3]);
First of all, you can use a record and call fillchar(myrecord,sizeof(myrecord),0) but it may be error prone if you have some internal reference-counted values (like string).
But in your case, since it is only double values, it may be very easy to write:
procedure Initialize;
var localdata: record
a1, a2, a3, a4, a5, b1, b2, b3, b4, b5: Double;
obj: TObject;
i1, i2, i3, i4: integer;
end;
begin
fillchar(localdata,sizeof(localdata),0);
with localdata do
begin
a1 := 10;
a2 := a1+10;
assert(obj=nil);
inc(i1,20);
i2 := i1+10;
assert(i2=30);
end;
end;
As you can see, you can even mix types within the record. The trick is that you define your record type inline, without any type definition, which is not needed.
I admit this is not the direct answer, but I humbly suggest that you change your design to switch to something more "OOP-compatible".
Just use a dynamic array, or a class to embed the values. They will be all set to 0 by default.
For a dynamic array:
var a,b: array of double;
SetLength(a,5); // then use a[0] instead of a1, a[2] instead of a2...
SetLength(b,5); // then use b[0] instead of b1, b[2] instead of b2...
For a class - which is my preferred, since you can embedd your code within your data, as good objects:
type
TMyClass = class
public
a1, a2, a3, a4, a5, b1, b2, b3, b4, b5: Double;
procedure OneMethodHere;
function OneTestHere(aValue: double): boolean;
end;
var C: TMyClass;
C := TMyClass.Create; // every C member will be set to 0
try
if C.OneTestHere(10) then
C.OneMethodHere;
// you can use C.a1 or C.b5
finally
C.Free;
end;
Is it possible to combine two Bytes to WideChar and if yes, then how?
For example, letter "ē" in binary is 00010011 = 19 and 00000001 = 1, or 275 together.
var
WChar: WideChar;
begin
WChar := WideChar(275); // Result is "ē"
var
B1, B2: Byte;
WChar: WideChar;
begin
B1 := 19;
B2 := 1;
WChar := CombineBytesToWideChar(B1, B2); // ???
How do I get WideChar from two bytes in Delphi?
WChar := WideChar(MakeWord(B1, B2));
You should just be able to create a type and cast:
type
DoubleByte = packed record
B1: Byte;
B2: Byte;
end;
var
DB: DoubleByte;
WC: WideChar;
begin
DB.B1 := 19;
DB.B2 := 1;
WC = WideChar(DB);
end;
Failing a cast you can use Move() instead and simply copy the memory.
I am trying to get the upper 4 bits of a Byte.
That is my attempt so far:
function Upper4Bits(const X : Byte): Byte;
type
BS = set of 0..7;
var
K : Byte; Q: BS;
begin
Q := [];
for K := 0 to 3 do {is it right? upper?}
{what i need here?}
Include(Q, {what i put here});
Upper4Bits := Byte(Q)
end;
Thanks In Advance.
According to your comment to kotlinski's answer, you want result := (byte1 and $F0) or (byte3 and $0F).
How about Upper4Bits := X Shr 4;?
I have TBytes variable with a value [0,0,15,15]. How can I convert it to "00FF" ?
I dont want to use loops, bcoz this logic to be used in time intensive function.
(I tried using BinToHex, but I could not get it working with string variable.)
Thanks & Regards,
Pavan.
// Swapping is necessary because x86 is little-endian.
function Swap32(value: Integer): Integer;
asm
bswap eax
end;
function FourBytesToHex(const bytes: TBytes): string;
var
IntBytes: PInteger;
FullResult: string;
begin
Assert(Length(bytes) = SizeOf(IntBytes^));
IntBytes := PInteger(bytes);
FullResult := IntToHex(Swap32(IntBytes^), 8);
Result := FullResult[2] + FullResult[4] + FullResult[6] + FullResult[8];
end;
If that last line looks a little strange, it's because you requested a four-byte array be turned into a four-character string, whereas in the general case, eight hexadecimal digits are required to represent a four-byte value. I'm simply assumed that your byte values are all below 16, so only one hexadecimal digit is needed. If your example was a typo, then simply replace the last two lines with this one:
Result := IntToHex(Swap32(IntBytes^), 8);
By the way, your requirement forbidding loops will not be met. IntToHex uses a loop internally.
function ByteToHex(InByte:byte):shortstring;
const Digits:array[0..15] of char='0123456789ABCDEF';
begin
result:=digits[InByte shr 4]+digits[InByte and $0F];
end;
Example :
MyHex := ByteTohex($FF);
the result
MyHex is "FF".
MyHex := ByteTohex(255);
the result
MyHex is "FF".
MyHex := ByteTohex($55);
the result
MyHex is "55".
This one is quite fast and works with any array size.. It's like BinToHex, but instead of expecting 0..255 byte values, it only uses the low nibble.
procedure BinToSingleHex(Buffer, Text: PAnsiChar; BufSize: Integer);
const
Convert: array[0..15] of AnsiChar = '0123456789ABCDEF';
var
I: Integer;
begin
for I := 0 to BufSize - 1 do
begin
Text[0] := Convert[Byte(Buffer[I]) and $F];
Inc(Text);
end;
end;
Assembler that does the same:
procedure BinToSingleHex(Buffer, Text: PAnsiChar; BufSize: Integer);assembler;
asm
PUSH ESI
PUSH EDI
MOV ESI,EAX
MOV EDI,EDX
MOV EDX,0
JMP ##1
##0: DB '0123456789ABCDEF'
##1: LODSB
AND DL,AL
AND DL,0FH
MOV AL,##0.Byte[EDX]
STOSB
DEC ECX
JNE ##1
POP EDI
POP ESI
end;
usage:
type THexDigit=0..15;
const ArSize=16;
var Ar:array[0..Pred(ArSize)] of THexDigit=(0,1,2,3,4,5,6,7,8,9,8,7,6,5,4,3);
S:Array[0..Pred(ArSize)] of AnsiChar;
BinToSingleHex(#Ar,S,Length(Ar));
WriteLn(S);
Bit late to the party but why not a simple lookup table?
const
HexChars : Array[0..15] of Char = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
Assuming TBytes values of 0..15
Function (ABytea: TBytes): string
begin
Result := HexChars[ABytea[0]];
Result := Result + HexChars[ABytea[1]];
Result := Result + HexChars[ABytea[2]];
Result := Result + HexChars[ABytea[3]];
end;
of course neater with a loop :) and needs modifying for byte values above 15:
begin
Result := HexChars[ABytea[0] shr 4];
Result := Result + HexChars[ABytea[0] and $0F];
Result := Result + HexChars[ABytea[1] shr 4];
Result := Result + HexChars[ABytea[1] and $0F];
Result := Result + HexChars[ABytea[2] shr 4];
Result := Result + HexChars[ABytea[2] and $0F];
Result := Result + HexChars[ABytea[3] shr 4];
Result := Result + HexChars[ABytea[3] and $0F];
end;
Still neater with a loop especially if TBytes gets larger
I had the same problem. My solution using System.SysUtils.TByteHelper.ToHexString (with loop)
function ToHexString(const MinDigits: Integer): string; overload; inline;
Example code:
procedure TForm1.Button2Click(Sender: TObject);
begin
var text:string;
var w:integer:=0;
var bytearray: Tarray<byte>:= [$DE, $AD, $BE, $EF];
repeat
text:= text+ pbyte(#bytearray[w])^.ToHexString(2);
inc(w);
until w >= high(bytearray);
end;
bytearray := $DE $AD $BE $EF
text := DEADBEEF