Delphi custom format string - delphi

I have a string that contains 8 to 12 characters (alphanumeric). I would like to use the Format function to format it such that after first 4 characters a hyphen to be inserted and after next 3 characters another hyphen to be inserted:
cccc-ccc-c
if string has 8 chrs
cccc-ccc-cc
if string has 9 chrs
cccc-ccc-ccc
if string has 10 chrs
cccc-ccc-cccc
if string has 11 chrs
cccc-ccc-ccccc
if string has 12 chrs
Is it possible to use a single lined Format function to acquire the effect? I admit that the usage of Format function is beyond my grasp.

The function you are looking for is FormatMaskText located in System.MaskUtils. The Mask to be used is 'cccc-ccc-ccccc;0;'.

Use Insert instead of Format:
Insert(s, '-', 5);
Insert(s, '-', 9);

There is no built-in format specifier (or combination of them) that will do the formatting you're looking to do.
You can, of course, write your own function to do so (name it, of course, with something meaningful to the values you're formatting):
function MyFormat(Value: string): String;
begin
Assert(Length(Value) >= 8);
Result := System.Insert(Value, '-', 5);
Result := System.Insert(Result,'-', 9);
end;
Use it:
Value := MyFormat('12345678'); // Returns '1234-567-8'
Value := MyFormat('123456789'); // Returns '1234-567-89'
Value := MyFormat('1234567890'); // Returns '1234-567-890'
If you insist on trying to do it with Format, you need multiple calls to Copy (although you can skip the first one by using a width specifier). These can be done, of course, on a single line; I've spread it across multiple just for formatting here to eliminate horizontal scrolling.
Str := '12345678';
Value := Format('%.4s-%s-%s',
[Str,
Copy(Str, 5, 3),
Copy(Str, 8, MaxInt)]); // Return '1234-567-8'
Str := '1234567890';
Value := Format('%.4s-%s-%s',
[Str,
Copy(Str, 5, 3),
Copy(Str, 8, MaxInt)]); // Return '1234-567-890'
There is no way to use a "width specifer" type method to extract substrings within a string, though. (You can extract the first n characters using %.ns, but you can't do the first n characters starting at the fourth with any combination of specifiers.)

Related

What is the correct way to copy the dictionary?

I need to check if there has been a change in a certain part of the application and therefore I make "copies" of the data after loading them and then compare them. One part of the comparison function involves checking keys in dictionaries like lDict1.Keys.EqualsTo(lDict2.Keys).
Although the dictionaries do not rely on the order of the elements, I didn't realize that even if I fill two dictionaries with the same data, they won't be created the same and the order of elements may change, so the previous function does not work properly because it relies on the elements order that may not match when using any of the following methods. (I'm not sure why)
var
lDict1, lDict2 : IDictionary<Integer, TObject>;
lKey : Integer;
begin
lDict1 := TCollections.CreateDictionary<Integer, TObject>;
lDict1.Add(5, nil); // Keys.First = 5, Keys.Last = 5
lDict1.Add(6, nil); // Keys.First = 5, Keys.Last = 6
lDict2 := TCollections.CreateDictionary<Integer, TObject>;
lDict2.AddRange(lDict1); // Keys.First = 6, Keys.Last = 5
lDict2.Clear;
for lKey in lDict1.Keys do // Keys.First = 6, Keys.Last = 5
lDict2.Add(lKey, nil);
end;
Is there any way to make an exact copy of the dictionary so I can compare them? One way to work around this problem is to create my own comparison function, but I'd like to avoid that.
function ContainsSameValues<T>(AEnumerable1, AEnumerable2: IEnumerable<T>): Boolean;
var
lValue : T;
begin
Result := AEnumerable1.Count = AEnumerable2.Count;
if Result then
begin
for lValue in AEnumerable1 do
begin
Result := AEnumerable2.Contains(lValue);
if not Result then
Exit;
end;
end;
end;
usage
ContainsSameValues<Integer>(lDict1.Keys, lDict2.Keys);
Checking for equality of a unordered dictionaries is a relatively simple algorithm. I will outline it here. Suppose we have two dictionaries, A and B.
Compare the number of elements of A and B. If this differs, the dictionaries are not equal.
Enumerate each key/value pair k,v in A. If k is not in B, or B[k] is not equal to v, then the dictionaries are not equal.
If you reach the end of the enumeration, then you know that the dictionaries are equal.

Easy way to split string into map in go

I have such string:
"k1=v1; k2=v2; k3=v3"
Is there any simple way to make a map[string]string from it?
You will need to use a couple of calls to strings.Split():
s := "k1=v1; k2=v2; k3=v3"
entries := strings.Split(s, "; ")
m := make(map[string]string)
for _, e := range entries {
parts := strings.Split(e, "=")
m[parts[0]] = parts[1]
}
fmt.Println(m)
The first call will separate the different entries in the supplied string while the second will split the key/values apart. A working example can be found here.

Bit Manipulation Delphi in XML - Bitwise

I am a student in high school and I am currently learning in Delphi XE3. We are learning about BIT manipulation. We have an assignment and while I have read a lot on the subject and understand the entire process of storing information in Bits and SHL/SHR I am having difficulty understanding how to do this process in Delphi.
The assignment is as follows:
Decimal Hexidecimal Binary
1 0x0001 0000000000000001
2 0x0002 0000000000000010
4 0x0004 0000000000000100
Passing an integer value in an XML file to identify the options set. For example. If I wanted to send option 1 and option 2, I would add 1+2=3. I would send 3 as the number to specify that options 1 and 2 are true.
On the client the binary value would be 0000000000000011 = 3
From what I have read I need to use a mask but I do not understand how to do this. How would do I use masks in Delphi ot obtain the individual values which would be True or False.
I tried doing this in a regular Integer variable but it always gets treated as an Integer and the result is very strange. If I convert the integer to a binary string representation and I iterate thru the characters the result is correct but I am assuming that I should not be doing this with strings. Any help or an example would be greatly appreciated. Thank you.
You usually check if a particular bit is set in a Integer variable using the and binary operator, and you set individual bits using the or operator, like this:
const
OPTION_X = $01;
OPTION_Y = $02;
OPTION_Z = $04;
var
Options: Byte;
begin
Options := OPTION_X or OPTION_Y; //actually 3, like in your example
//check if option_X is set
if (Options and OPTION_X) = OPTION_X then
ShowMessage('Option X is set'); //this message is shown, because the bit is set
//check if option_Z is set
if (Options and OPTION_Z) = OPTION_Z then
ShowMessage('Option Z is set'); //this message is NOT shown
end;
The different OPTION_ constants, are actually masks, in the sense they are used to mask bits to zero (to check if a particular bit is set) or to mask bits to 1 (to set a particular bit).
Consider this fragment:
begin
..
if cbOptionX.Checked then
Options := Options or OPTION_X;
..
the or will mask the first bit to 1. If we start with a Options value (in binary) of 01010000, the resulting Options would be 01010001
01010000
OR 00000001 //OPTION_X
= 01010001
the same value is used to mask all the other bits to 0 to check if a particular bit is set. The if condition, for example: (Options and OPTION_Z) = OPTION_Z, does this:
first it MASKS all the non-interesting bytes of the Option variable to 0. If we consider the last value of 01010001, the operation will result in clearing all the bits, but the first.
01010001
AND 00000001
= 00000001
considering a starting value of 01010000 it will return zero:
01010000
AND 00000001
= 00000000
next, it compares if that value is equal to the mask itself. If it is equal, the bit was set in the original Options variable, otherwise it was not set. If your mask contains only one bit, that's matter of taste, you can just check if the resulting value is, for example, different than 0, but if your mask contains multiple bits and you want to check if all the bits was set, you have to check for equality.
Delphi has a predefined type TIntegerSet which allows to use set operators. Assuming that options is an Integer, you can check if any bit (0-based) is set like this:
option1 := 0 in TIntegerSet(options); { Bit 0 is set? }
option3 := 2 in TIntegerSet(options); { Bit 2 is set? }
Changing the options is done via Include or Exclude:
Include(TIntegerSet(options), 0); { set bit 0 }
Exclude(TIntegerSet(options), 2); { reset bit 2 }
Of course you can use any other set operator that may be helpful.
Delphi has Bitwise Operators for manipulating individual bits of integer types. Look at the shl, shr, and, or, and xor operators. To combine bits, use the or operator. To test for bits, use the and operator. For example, assuming these constants:
const
Option1 = 0x0001;
Option2 = 0x0002;
Option3 = 0x0004;
The or operator looks at the bits of both input values and produces an output value that has a 1 bit in places where either input value has a 1 bit. So combining bits would look like this:
var
Value: Integer;
begin
Value := Option1 or Option2;
{
00000000000000000000000000000001 Option1
00000000000000000000000000000010 Option2
-------------------------------- OR
00000000000000000000000000000011 Result
}
...
end;
The and operator looks at the bits of both input values and produces an output value that has a 1 bit only in places where both input value have a 1 bit, otherwise it produces a 0 bit instead. So testing for bits would look like this:
var
Value: Integer;
Option1Set: Boolean;
Option2Set: Boolean;
Option3Set: Boolean;
begin
Value := 7; // Option1 or Option2 or Option3
Option1Set := (Value and Option1) = Option1;
{
00000000000000000000000000000111 Value
00000000000000000000000000000001 Option1
-------------------------------- AND
00000000000000000000000000000001 Result
}
Option2Set := (Value and Option2) = Option2;
{
00000000000000000000000000000111 Value
00000000000000000000000000000010 Option2
-------------------------------- AND
00000000000000000000000000000010 Result
}
Option3Set := (Value and Option3) = Option3;
{
00000000000000000000000000000111 Value
00000000000000000000000000000100 Option3
-------------------------------- AND
00000000000000000000000000000100 Result
}
...
end;

String comparison in Delphi

I have two strings, which I need to compare for equality.
String 1 is created in this way:
var
inBuf: array[0..IN_BUF_SIZE] of WideChar;
stringBuilder : TStringBuilder;
mystring1:string;
...
begin
stringBuilder := TStringBuilder.Create;
for i := startOfInterestingPart to endOfInterestingPart do
begin
stringBuilder.Append(inBuf[i]);
end;
mystring1 := stringBuilder.ToString();
stringBuilder.Free;
String 2 is a constant string 'ABC'.
When string 1 is displayed in a debug console, it is equal to 'ABC'. But the comparisons
AnsiCompareText(mystring1, 'ABC')
mystring1 = 'ABC'
CompareStr(mystring1, 'ABC')
all report inequality.
I suppose that I need to convert string 2 ('ABC') to the same type as the string 1.
How can I do that?
Update 26.09.2012:
aMessage is displayed in the log output as {FDI-MSG-START-Init-FDI-MSG-END}
Here's the code for printing the length of strings:
StringToWideChar('{FDI-MSG-START-Init-FDI-MSG-END}', convString, iNewSize);
...
OutputDebugString(PChar('Len (aMessage): ' + IntToStr(Length(aMessage))));
OutputDebugString(PChar('Len (original constant): ' + IntToStr(Length('{FDI-MSG-START-Init-FDI-MSG-END}'))));
OutputDebugString(PChar('Len (convString): ' + IntToStr(Length(convString))));
And here's the log output:
[3580] Len (aMessage): 40
[3580] Len (original constant): 32
[3580] Len (convString): 0
It looks like you're keeping garbage data in your wide string after the meaningful part, in your update, Length(aMessage) returns 40, while your source string's length is 32.
In Delphi a wide string is COM BSTR compatible, meaning it can hold null characters, a null does not terminate it, it keeps its length at a negative offset of the character data. A possible null character in it helps it to be converted to other string types, but it doesn't alter its own termination.
Consider the below,
const
Source = '{FDI-MSG-START-Init-FDI-MSG-END}';
var
ws: WideString;
size: Integer;
begin
size := 40;
SetLength(ws, size);
StringToWideChar(Source, PWideChar(ws), size);
// the below assertion fails when uncommented
// Assert(CompareStr(Source, ws) = 0);
ws := PWideChar(ws); // or SetLength(ws, Length(Source));
// this assertion does not fail
Assert(CompareStr(Source, ws) = 0);
end;

Scan entire process memory with ReadProcessMemory

I'm tryin to scan an entire process memory but no success... What I'm doing is: for tests I'm using notepad, so I write there %B and this values in HEX are: 25(%) and 42(B). So the code is:
while (VirtualQueryEx(PIDHandle, Pointer(MemStart), MemInfo, SizeOf(MemInfo)) <> 0) do
begin
if ((MemInfo.State = MEM_COMMIT) and (not (MemInfo.Protect = PAGE_GUARD)
or (MemInfo.Protect = PAGE_NOACCESS)) and (MemInfo.Protect = PAGE_READWRITE)) then
begin
SetLength(Buff, MemInfo.RegionSize);
if (ReadProcessMemory(PIDHandle, MemInfo.BaseAddress, Buff,
MemInfo.RegionSize, ReceivedBytes)) then
begin
for I := 0 to SizeOf(Buff) do
begin
if (IntToHex(Buff[i], 1) = '25') and (IntToHex(Buff[i+2], 1) = '42') then
Form1.Memo1.Lines.Append(IntToHex(Buff[i], 1));
end;
end;
end;
MemStart:= MemStart + MemInfo.RegionSize;
end;
CloseHandle(PIDHandle);
end;
The var 'Buff' is TBytes (I read about TBytes and think it's same as array of byte). So I'm converting the bytes to Hex, and searching for values: 25 and 42 respectively. The code is like:
if (IntToHex(Buff[i], 1) = '25') and (IntToHex(Buff[i+2], 1) = '42') then
Because have 00 between the hex values. So I need to add '+2'. How can I scan the entire memory for this values??
Notepad uses Unicode so you'll need to look for UTF-16 encoded data, $0025 and $0042.
I don't understand why you feel the need to convert into hex strings before comparing. There's nothing special about hex that requires the use of strings. Hexadecimal is just a number system with base-16. So, decimal 32 is the same as hexadecimal 20, i.e. 32=$20. Do your comparison directly with integral values:
if (Buff[i]=$25) and (Buff[i+2]=$42) then
That said, taking into account the $00 bytes your test should really be something like this:
var
Target: string;
....
Target := '%B';
if CompareMem(#Buff[i], #Target[1], Length(Target)*SizeOf(Char)) then
....
I don't want to get too deep into the rest of your code, but this line
for I := 0 to SizeOf(Buff) do
is wrong on many different levels.
SizeOf(Buff) returns the size of a pointer since a dynamic array variable is essentially just a pointer. A useful thing to remember is that SizeOf is evaluated at compile time.
If you used Length instead of SizeOf then you would be iterating over the end of the list. To loop over a dynamic array, loop from 0 to Length(...)-1.
But in this case you are accessing index i+2 inside the loop, so you should loop from 0 to Length(...)-3.
But in fact you need to compare against 4 consecutive bytes to find a match. Perhaps like this:
TargetByteLength = Length(Target)*SizeOf(Char);
for i := 0 to Length(Buff)-TargetByteLength do
if CompareMem(#Buff[i], #Target[1], TargetByteLength) then
....

Resources