I am searching for a Perl-like split-function in Delphi - delphi

I know there a many ways to split a String, so that you'll get a StringList.
But my problem is that I want to split every character of the string.
That means the following String:
'That is my Example String'
should be converted to an array/Stringlist or what so ever:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
T h a t i s m y E x a m p l e S t r i n g
In Perl or Java the delimiter field of the split-function is just be let empty like
for example:
Perl: #string=split("",$string);
Java: String[] myStringArray=myString.split("");
What would be the best way for Delphi to manage this?

Usually there is no real need for such function in Delphi, because Delphi string behaves as char array, every element is accessible by index, and char is assignment compatible with string. Thereby you can use s[i] instead of splited[i] in almost all cases.
If you do need special function to fill a list, it may look like
procedure SplitStringEx(const s: string; Splitted: TStrings);
var
i: Integer;
begin
Splitted.Clear;
for i := 1 to Length(s) do
{possible variant for fresh Delph versions
to take zero-based strings into account:}
//for i := Low(s) to High(s) do
Splitted.Add(s[i])
end;
usage
SplitStringEx('abc', Memo1.Lines);

Related

Is it Possible to convert a set of numbers to their ASCII Char values?

I can convert a string into the Ascii values with spaces in between.
Example: "I like carrots"
To: 73 32 108 105...
But I'm trying to figure out a way to take those NUMBERS and convert them BACK into their ascii Chars.
I'm using the latest version of Delphi (embarcadero) and am new to coding so pls help me out :)
You can use the Ord function in every character.
The Chr function returns the character for specified ASCII value.
Example
var
s: string;
c: char;
s:='I like carrots';
for c in s do
begin
Writeln(ord(c));
end;
Here is the code:
var
S1 : String;
S2 : String;
A : TStringDynArray; // Add System.Types in uses
N : String;
C : Char;
begin
// 'I like carrots' in ascii code separated by a space
S1 := '73 32 108 105 107 101 32 99 97 114 114 111 116 115';
A := SplitString(S1, ' '); // Add System.StrUtils in uses
S2 := '';
for N in A do
S2 := S2 + Char(StrToInt(N));
ShowMessage(S2);
end;
I don't know the complete problem but you could add code to check of valid input string.
I don't know much about delphi, but take a look at this link: http://www.delphibasics.co.uk/Article.asp?Name=Text
In the Assigning to and from character variables section it demonstrates a function like so:
fromNum := Chr(65); // Assign using a function.
If that doesn't work, you could consider building a large mapping of int->char. The beginnings of which is in that above website also near the top.

Delphi ORD function not returning expected value

I have 2 functions, one to encrypt and the other to de-crypt a string, that use the Ord() function. It works great except with extended Ascii codes.
If I use the letter ê (Ascii code 136), the Ord() function returns 234 where as I expected it to return 136.
If I run decrypt on the encrypted string, I get a different result than what the original string was, the ê turns into a j.
Can somebody please help on how to solve this?
procedure TForm1.btnEncryptClick(Sender: TObject);
var
sTempString : string;
iIndex,
i: integer;
begin
sTempString := edtOriginalString.Text ;
for iIndex := 1 to length(sTempString) do
begin
i := ord(sTempString[iIndex]);
i := i shl 1;
sTempString[iIndex] := Char(i);
end;
edtEncryptedString.Text := sTempString;
end;
procedure TForm1.btnDecryptClick(Sender: TObject);
var
sTempString : string;
iIndex,
i : integer;
begin
sTempString := edtEncryptedString.Text ;
for iIndex := 1 to length(sTempString) do
begin
i := ord(sTempString[iIndex]);
i := i shr 1;
sTempString[iIndex] := char(i);
end;
edtDecryptedString.Text := sTempString;
end;
If I use the letter ê (Ascii code 136)
No, that's actually wrong. ASCII only has 128 characters (0 to 127).
However, ê is the Unicode character U+00EA: LATIN SMALL LETTER E WITH CIRCUMFLEX.
And EA (hex) is indeed 234 (dec).
Delphi characters and strings are 8-bit before Delphi 2009, and Unicode in Delphi 2009 and later.
So in your case, Delphi 6, a character is 8-bit.
Hence, your left shift will make you lose the most significant bit (MSB), and you cannot possibly hope to get it back.
Indeed, if we take the case of ê (234), we have
1110 1010 (ê)
Shifting the bits one step to the left, we obtain
1101 0100
Shifting the bits one step to the right, we obtain
0110 1010 (j).
Hence, we lost information.
However, your method will work for ASCII characters (<= 127), because they all have zero as the MSB. It will not work for any characters above 127, because they all have one as the MSB (so it wouldn't work even if Ord did indeed return 136 in your case).
Hence, you need to abandon or redesign your "encryption" method if you want to support characters above 127. For instance, you could rotate the bits instead of shifting them. Or you could invert them (using not).
If you choose to rotate instead of shift, you will get this:
1110 1010 (ê)
rotate left:
1101 0101
rotate back (right):
1110 1010 (ê)
Although it isn't relevant to your actual issue, you might still wonder why Ord doesn't return 136 as you'd expect.
Well, before Unicode (mainly in the 1990s and earlier), there simply were many different (non-compatible) character encodings. Often, an 8-bit encoding/codepage (characters 0..255) included the ASCII characters (0..127) and then made its own choices for the remaining characters (128..255). Since ê isn't an ASCII character, this means that only some of these "extended ASCII" codepages might include ê, and among those that do include ê, the actual numeric values might very well differ.
In other words, your source claiming that ê is 136 and your Delphi program are using different 8-bit codepages.
In the modern world of Unicode, this kind of problem no longer exists.

Enumeration set size in x64

I found that a SizeOf(set) i different in 32-bit and 64-bit, the example below shows 5 byte for 32-bit and 8 for 64-bit. But i found nothing information about changes in SizeOf(sets) for 64-bit. Is there any Embarcadero documentation about it or compiler directive to get a similar results on 32 and 64-bit.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses System.SysUtils;
type
{ Enumeration of properties}
TProperty1 = (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14,
p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28,
p29, p30, p31, p32, p33, p34, p35, p36, p37);
TProperties1 = set of TProperty1;
begin
WriteLn(SizeOf(TProperties1));
ReadLn;
end.
To answer your question. I couldn't find anything on the Embarcadero site regarding the differences or a compiler directive to change the behavior. My research indicates the following:
Sets have the following sizes in bytes in 32 bit:
Up to 8 elements - 1 Byte
9 to 16 elements - 2 Bytes
17 to 32 elements - 4 Bytes
From this point onwards it adds adds bytes as needed, one at a time. So 33 to 40 elements uses 5 bytes and 41 to 48 elements uses 6 bytes.
In 64 bit mode, things are slightly different:
Up to 8 elements - 1 Byte
9 to 16 elements - 2 Bytes
17 to 32 elements - 4 Bytes
33 to 64 elements - 8 Bytes
From this point onwards it adds adds bytes as needed, one at a time. So 65 to 72 elements uses 9 bytes and 73 to 80 elements uses 10 bytes.
To get around this you are going to need to either use something like WriteSet in TWriter.WriteProperty and TReader.ReadSet or you can do something like this:
procedure SaveSetToStream(aStream: TStream; const aSet: TProperties1);
var
streamData: array[0..7] of byte;
begin
Assert(SizeOf(aSet) <= SizeOf(streamData), 'Set is too large to save. Increase the array length.');
FillChar(streamData, SizeOf(streamData), 0);
Move(aSet, streamData, SizeOf(aSet));
aStream.Write(streamData, SizeOf(streamData));
end;
function ReadFromStream(aStream: TStream): TProperties1;
var
streamData: array[0..7] of byte;
begin
Assert(SizeOf(Result) <= SizeOf(streamData), 'Set is too large to load. Increase the array length.');
aStream.Read(streamData, SizeOf(streamData));
Move(streamData, Result, SizeOf(Result));
end;
Another workaround, to make sure a 32 bit machine can read a stream from a 64 bit machine and vice-versa is to create a function
function SizeCheck( const p : integer ) : integer;
begin
if p in [5..8 ] then Result := 8 else Result := p; // adjust for 64 bit set sizes
end;
and then use
Stream.Write(set, SizeCheck(SizeOf(set)));
Obviously only use for sets.

Translating I/O, VB6 to Delphi

I am taking VB6 code and translating it to Delphi.
VB6 code that opens a file using sequential order:
Dim bytNumDataPoints As Byte
Dim bytcount as Byte
Dim lintData(0 To 23) As Long
Input #intFileNumber, bytNumDataPoints
For bytcount = 0 To (bytNumDataPoints - 1)
Input #intFileNumber, lintData(bytcount) '
lintData(bytcount) = (lintData(bytcount) + 65536) Mod 65536
Next bytcount
File data:
24 <<<<<<<<<<<< Number Data Points
200 300 400 450 500 600 750 1000 1250 1500 1750 2000 2500 3000 3500 3750 4000 4500 5000 5250 5500 5750 6000 6250 <<<< data
This is some neat stuff. You keep calling Input and fill out the array.
As far I know there is no equivalent for this phenomena in Delphi. You cannot use ReadLn like that, right? To me in Delphi you would have to
ReadLn(F, S); //S is a string
z.Delimiter := ' '; //z is a stringlist
z.DelimitedText := S; //and then breakdown the array
Any thought? Thanks.
Use Read instead of Readln;
Something like this:
var
ArrLng, Index: Integer;
Arr: array of Integer;
F: Text;
begin
Assign(F, 'your-fie-name');
OpenFile(F);
try
Readln(F, ArrLng);
SetLength(Arr, ArrLng);
Index := 0;
while (not Eof(F)) and (Index < ArrLng) do
begin
Read(F, Arr[Index]);
Inc(Index);
end;
finally
CloseFile(F);
end;
end;
Splitting string would be generally better approach, with StringList or without.
How to split a string of only ten characters e.g."12345*45688" into an array
Split a string into an array of strings based on a delimiter
How to split the string in delphi
But i think you can also use old Pascal approach there. Just forget about end-of-line, you probably don't need it.
var F: TextFile; I, J, K: integer;
begin
...
ReadLN(F, J);
for i := 1 to J do
Read(F, K);
...
end
But i think it would only be nice for niche approaches (like out of memory) and overall SplitString approach would be faster.
And if you have multi-line files, then two chained stringlist would be IMHO most easy approach, providing those files are not GB-sized.
https://stackoverflow.com/a/14454614/976391
or utilizing more SL features - https://stackoverflow.com/a/14649862/976391
The TStringList class and it's LoadFromFile method. It's got delimiter properties as well.
When you are doing Delphi, treat it like .net. If there's something that should be there, it is, you just haven't found it yet.
You can chuck the number of values thing away as well.

Upgrade for file of records and backward compatability

I have such file:
file of record
Str: string[250];
RecType: Cardinal;
end;
but after some time using of this file my customer found, that Str never be bigger than 100 chars and also he need additional fields.
In new version we have such file:
file of packed record
Str: string[200];
Reserved: array[1..47] of Byte;
NewFiled: Cardinal;
RecType: Cardinal;
end;
This record have the same size, in previous record between Str and RecType was one unused byte when aligned to 8 bytes.
Question: what happened, when this new file will be readed from old code? He need backward compatability.
Old code reading sample:
var
FS: TFileStream;
Rec: record
Str: string[250];
RecType: Cardinal;
end;
...
// reading record by record from file:
FS.Read(Rec, SizeOf(Rec));
The old school pascal string use the first byte of the string (index 0) to store the length of the string.
Let's look at the memory of this record:
byte 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ........ 243..246 247..250
value 10 65 66 67 68 69 70 71 72 73 74 0 200 130 NewField RecType
From byte 11 to 242, the memory can contain garbage, it is simply ignored by the program (never shown) as this takes the value 10 at the byte 0 as the length of the string, so the string becomes 'ABCDEFGHIJ'
This ensures the old program reading a file created with the most recent version will never see garbage at the end of the strings, since the view of that strings will be limited to the actual size of the string and that memory positions are just ignored.
You have to double check if the old program does not change the values stored in case it writes the records back to the file. I think it is also safe, but I'm just not sure and have no Delphi at hand to test.

Resources