How strings differ from massive(array) of chars in Pascal? - arrays

I have a problem. Why I can't assign the value of strings to strings, but with chars it works. Why ^^^^^^? Where's string? Why There's the a[i]?
Is it because internal representation of strings and chars?
program massive.pas;
type
chars = array [1..255] of char;
var
s,s1: string;
ch1,ch2: chars;
i: integer;
begin
s1 := '';
s := 'abrakadabra';
for i := 1 to 5 do
begin
s1[i] := s[i];
writeln(s1[i],#10,'^^^',s1,'^^^')
end;
ch2 := '';
ch1 := 'abrakadabra';
for i := 1 to 5 do
begin
ch2[i] := ch1[i]
writeln(ch2[i])
end;
writeln('%%%',ch2,'%%%');
for i := 1 to 5 do
writeln('&&&',s1[i],'&&&');
end.
*Output
a
^^^^^^
b
^^^^^^
r
^^^^^^
a
^^^^^^
k
^^^^^^
a
b
r
a
k
%%%abrak%%%
&&&a&&&
&&&b&&&
&&&r&&&
&&&a&&&
&&&k&&&

The main difference between type chars = Array[1..255] of Char and String is that a chars array has a fixed length, while the string has a dynamic length.
You did not say which compiler you use, but I do think that the String type is what in some Pascal editions is called a ShortString, with a max length of 255 chars. The space for 255 chars is preallocated and the structure includes a length field, that keeps track of assigned length of the string.
In your example, you assign s1 := ''; in other words the length is set to zero. Then you do a mistake in the for loop in assigning s1[i] := s[i]; without setting the length of s1.
Subsequent reading of s1 always return an empty string as the length field is 0.
If you would assign the characters to the string, e.g. as:
for i := 1 to 5 do
begin
SetLength(s1, Length(s1)+1);
s1[i] := s[i];
writeln(s1[i],#10,'^^^',s1,'^^^');
end;
then the result would be what you originally expected.
Still better to set the length to the final 5 before the for loop.
Of course there are other solutions too. One is to not set the length at all, but to concatenate the string in the loop and let it handle the length field by itself:
for i := 1 to 5 do
begin
s1 := s1 + s[i];
writeln(s1[i],#10,'^^^',s1,'^^^');
end;
Edit 24.12.2021:
In a comment you said: But I still don't understand, why, when i wrote s1[1] in the for loop, all worked?
...and presumably refer to this code just before end.:
for i := 1 to 5 do
writeln('&&&',s1[i],'&&&');
We need to look at the memory layout and know that the first byte of the memory allocated to s1 is the length of the string. It can be referred to as s[0]. Subsequent bytes hold the characters that make up the stored string and they can be referred to as s[1]..s[n].
The first byte was set to 0 when you wrote (in the very beginning):
s1 := '';
// memory content:
0
|_|_|_|_|_|_|_|_|_| ...
Then you added the characters to s1 by manipulating the memory directly when you wrote in the first for loop:
s1[i] := s[i];
// content after 5 characters
0 a b r a k
|_|_|_|_|_|_|_|_|_| ...
Because you did not use concatenation (or adjusted the length while you added the characters) the length is still 0.
Then at the end in the last loop, you fetch the characters again by accessing the memory directly, and get the result you do, seemingly correct, but badly misusing the string structure.

Related

Read() not working for string type inputs in an array

I'm new to pascal and I have been working on a project recently. And here's a sample of it to where I got the problem. I'm trying to assign string type input values to string type array but it wont work. Basically, it wont allow me to enter data. It skips read(). Cannot enter data.But it works when I change the data type in both of them to Integer.
program temp;
var input_array : array[0..5] of string;
k : integer;
y : string;
begin
for k := 0 to 5 do
begin
writeln('Enter character : ');
read(y); // Not working
input_array[k] := y;
end;
for k := 0 to 5 do
writeln(input_array[k]);
end.
The FPC is not a fully ISO-compliant compiler, yet it’s worth taking a look at the standards anyway. In ISO 10206 “Extended Pascal” you will find the following note:
e) If v is a variable‑access possessing a fixed‑string‑type of capacity c, read(f, v) shall satisfy the following requirements. [… super technical mumbo jumbo …]
NOTE — 6 If eoln(f) is initially true, then no characters are read, and the value of each component of v is a space.
Bottom line, you can “fix” your program by inserting the following conditional readLn which will “consume” any “remnant” end‑of‑line character prior your actual read:
for k := low(input_array) to high(input_array) do
begin
write('Enter character: ');
if EOLn then
begin
readLn;
end;
read(input_array[k]);
end;

Usage of non UTF-8-encoded string as map key

I would like to use a bytearray of variable length as key within a map.
myMap := make(map[[]byte]int)
As slices and variable length bytearrays are no valid key type in go, the code above is not valid.
Then I read that strings are just a set of 8-bit bytes, conventinally but not necessarily representing UTF-8-encoded text.
Are there any problems to use such a non UTF-8-encoded string for a map key regarding hashing?
The following code demonstrates how I converted []byte to string and back to []byte again:
package main
import (
"bytes"
"fmt"
)
func main() {
// src is a byte array with all available byte values
src := make([]byte, 256)
for i := 0; i < len(src); i++ {
src[i] = byte(i)
}
fmt.Println("src:", src)
// convert byte array to string for key usage within a map
mapKey := string(src[:]) // <- can this be used for key in map[string]int?
//fmt.Println(mapKey) // <- this destroys the print function!
fmt.Printf("len(mapKey): %d\n", len(mapKey)) // <- that actually works
// convert string back to dst for binary usage
dst := []byte(mapKey)
fmt.Println("dst:", dst)
if bytes.Compare(src, dst) != 0 {
panic("Ups... something went wrong!")
}
}
There is no problem using string as key in a map where the string is not valid UTF-8.
The Go Blog: Strings, bytes, runes and characters in Go:
In Go, a string is in effect a read-only slice of bytes.
And Spec: Comparison operators:
String values are comparable and ordered, lexically byte-wise.
What matters is what bytes the string has, may it be valid or non-valid UTF-8 sequence. If 2 string values have the same invalid UTF-8 byte sequence, they are equal, and if not, they aren't.
Testing invalid and valid sequences ("\xff" and "\x00"):
m := map[string]byte{}
m["\xff"] = 1
m["\x00"] = 2
fmt.Println(m["\xff"], m["\x00"])
Output is (try it on the Go Playground):
1 2

delphi x32 and x64, (typecast?) array of bytes into (wide)string

I receive from an external function(dll) widestring in array of bytes.
to convert the bytes to string, I use the following simple code:
mystrvar := widestring(buffer);
where buffer is the byte array.
when compile for 32bits, it works great, but when compile this for 64bits code returns empty string while the buffer(byte array) is the same in both cases.
the same happens when use
mystrvar := string(buffer);
while pchar(buffer) or pwchar(buffer) works.
Reason why I do not use pwchar is;
pwchar(buffer) breaks by 00 while widestring(buffer) does not. This buffer(bytearray) contains stringlist which is delimited by (00).
btw, excuse me for bad english.
Use
SetString(mystrvar,buffer,LENGTH(buffer) DIV SizeOf(WideChar));
assuming that
VAR
mystrvar : WideString;
buffer: ARRAY OF BYTE;
and that "buffer" does not contain a trailing zero-terminating set of bytes. Also note that "buffer" is an array of BYTEs and thus the length of the buffer is twice that of the length of the resulting string.
Assuming your array is double Null Terminating you can use this:
while Buffer^ <> WideNull do
begin
value := PWChar(Buffer);
CommaText := CommaText + value + ',';
Inc(Buffer, (Length(value) + 1));
end;

Carry over incrementing bytes in an array

I'm testing to see how easily (or quickly rather) our encryption can be cracked. The encryption key is an array of byte (could be any variable length). So, starting from an array populated with all 0's, I need to increment one byte at a time starting with the first. When any byte reaches its max and goes back to 0, the next byte in the array needs to increment (carry over).
It would be simple if the array were a short fixed length - but I'm clueless how to do so in a variable array length.
Variable declared inside the thread:
FKey: array of Byte;
After each thread iteration, it calls this procedure to increment the key by one byte:
procedure TCrackThread.NextKey;
begin
//Increment first byte
//If max, reset to 0 and increment next byte
//Recursively check the same for each following byte in array
end;
How can I increment (starting from the first) the bytes and carry over to the next in this variable length array?
This snippet will increase the first element and will continue to do so with the array elements as long as they are 255. If so, they are reset to zero. The routine will stop as soon as the condition is not fulfilled or the index reached maximum.
var
k: Integer;
...
if (Length(fKey) > 0) then begin
k := 0;
Inc(fKey[k]);
while (fKey[k] = 255) do begin
fKey[k] := 0;
Inc(k);
if (k >= Length(fKey)) then
break;
Inc(fKey[k]);
end;
end;
This would convert an array 254,0,0 into 0,1,0
If you want a carry to ripple through before the first increment,
this code will do that:
procedure Next;
var
k: Integer;
carry: Boolean;
begin
if (Length(fKey) > 0) then begin
k := 0;
repeat
carry := (fKey[k] = 255);
if carry then begin
fKey[k] := 0;
Inc(k);
if (k >= Length(fKey)) then
break;
end
else
Inc(fKey[k]);
until not carry;
end;
end;
This will convert 255,255,0 into 0,0,1.

Inno Setup Acces violation increment array of strings

I hava an array of strings. I increment this array when it is necessary with the function:
function insertMessageAction(list: TMessagesActions; message: String): TMessagesActions;
var
lenght: integer;
begin
if message <> '' then begin
lenght := GetArrayLength(list);
SetArrayLength(list, lenght +1);
if GetArrayLength(list) > lenght then begin
list[lenght] := message
end;
end;
result := list;
end;
If de increment is of 0 to 2, no problems, by when I increment to 3 lenght, the array is corrupted and 'value' of list is: "Acces violation at address 00403498. Read of address 0000006A".
It is impossible to create more long arrays of 2 items (strings)? There is limit of characters?
Thanks.
First of all, do not use array for your task. Memory reallocation that happens when changing the size of your string array is an expensive operation. What's more, you were trying to return a copy of that input array (in a wrong way), which would be unecessarily inefficient too.
I strongly suggest you to use the TStringList which is intended to be used for a collection of strings. By using TStringList, the whole code from your question would become:
StringList.Add(Message);
But to your question. It is possible, but you need to comply with a few things; at least:
Do not attempt to return an input array passed by reference:
Result := list;
If you need to have an array as a return type for some reason, allocate the size of this output array and copy all the elements from the input array:
InputLen := GetArrayLength(List);
SetArrayLength(Result, InputLen + 1);
for I := 0 to InputLen - 1 do
Result[I] := List[I];
Result[InputLen] := Message;
If you'd still like to stay by array, better pass it by variable parameter:
[Code]
type
TMessagesActions = TArrayOfString;
procedure InsertMessageAction(var AList: TMessagesActions;
const AMessage: String);
var
ArrayLen: Integer;
begin
ArrayLen := GetArrayLength(AList);
SetArrayLength(AList, ArrayLen + 1);
AList[ArrayLen] := AMessage;
end;

Resources