Combine String and Character in Delphi - string

function leftstr(s:string; n:Integer):string;
var
i:integer;
t:string;
begin
//init var t
t := '';
for i := 0 to n do
begin
//main loop
t := t + s[i];
end;
//return results
Result:=t;
end;
So when I run this function and use ShowMessage to get the value of t in each increment of i, t is always blank. Can someone tell me the problem? This is compiled using Delphi XE6

The problem is that strings use 1-based indexing and you are accessing out of bounds. Your loop should be:
for i := 1 to n do
Probably what happens is that s[0] refers to some part of the string's meta data (length or reference count, I cannot remember which) that happens to contain a zero byte. This is then interpreted as a null terminator when the string is passed to ShowMessage.
If you had enabled the range checking feature in your compiler options, the compiler would have inserted code that would have found the error for you. This range checking feature is an astonishingly neglected and overlooked feature.
Of course, the function is a little pointless, as well as rather inefficient. You can use LeftStr from the StrUtils unit. Or plain old Copy from the System unit.

Related

Why does ShowMessage give unexpected output when my string contains #0 characters?

Today I stumbled upon something mysterious. This line of code:
showmessage(menuMain.player[2] + ' ready!');
Generates this message (For example menuMain.player[2] = Player):
Player
But if I put code this way (For example menuMain.player[2] = Player):
showmessage('Test: ' + menuMain.player[2]);
It will generate this message:
Test: Player
According to the debugger, the exact value of the string (buffer := menuMain.player[2] + ' ready!') is this:
'Player'#0#0#0#0#0#0#0#0' ready!'
I do honestly believe this is a compiler glitch, because I have the exact same line in another block of code, and it works flawlessly.
Now the tough part for me, is that me being dumb, or this is indeed a glitch?
the exact value of buffer string (buffer = menuMain.player[2] + ' ready!'): 'Player'#0#0#0#0#0#0#0#0' ready!'
The problem is the embedded null characters (the #0). Those characters act as string terminators, which means the string stops being processed at that point for most WinAPI functions, including those that output or paint text. When the first #0 is found, the string is considered to have ended.
You can test this easily enough yourself, with code something like this:
var
TestStr: string;
begin
TestStr := 'This is a test';
ShowMessage(TestStr); // Outputs This is a test
TestStr[5] := #0;
ShowMessage(TestStr); // Outputs This
end;
The remaining question, of course, is how did you end up with those embedded nulls in the first place? Since you've not posted the code that populates menuMain.player, it's impossible to say, but that's that's the area you need to inspect, because string array elements in Delphi do not contain nulls on their own. You can also check that yourself:
var
TestArr: array[1..2] of string;
begin
TestArr[1] := 'Player one';
TestArr[2] := 'Player two';
ShowMessage(TestArr[1] + ' defeated ' + TestArr[2]);
end;
So the answer to your question
Now the tough part for me, is that me being dumb, or this is indeed a glitch?
It's a glitch, but the glitch is somewhere in your code. It's not a glitch in Delphi or its strings.

ada split() method

I am trying to write an Ada equivalent to the split() method in Java or C++. I am to intake a string and an integer and output two seperate string values. For example:
split of "hello" and 2 would return:
"The first part is he
and the second part is llo"
The code I have is as follows:
-- split.adb splits an input string about a specified position.
--
-- Input: Astring, a string,
-- Pos, an integer.
-- Precondition: pos is in Astring'Range.
-- Output: The substrings Astring(Astring'First..Pos) and
-- Astring(Pos+1..Astring'Last).
--------------------------------------------------------------
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Strings.Fixed;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Strings.Fixed;
procedure Split is
EMPTY_STRING : String := " ";
Astring, Part1, Part2 : String := EMPTY_STRING;
Pos, Chars_Read : Natural;
------------------------------------------------
-- Split() splits a string in two.
-- Receive: The_String, the string to be split,
-- Position, the split index.
-- PRE: 0 < Position <= The_String.length().
-- (Ada arrays are 1-relative by default)
-- Passback: First_Part - the first substring,
-- Last_Part - the second substring.
------------------------------------------------
function Split(TheString : in String ; Pos : in Integer; Part1 : out String ; Part2 : out String) return String is
begin
Move(TheString(TheString'First .. Pos), Part1);
Move(TheString(Pos .. TheString'Last), Part2);
return Part1, Part2;
end Split;
begin -- Prompt for input
Put("To split a string, enter the string: ");
Get_Line(Astring, Chars_Read);
Put("Enter the split position: ");
Get(Pos);
Split(Astring, Pos, Part1, Part2);
Put("The first part is ");
Put_Line(Part1);
Put(" and the second part is ");
Put_Line(Part2);
end Split;
The main part I am having trouble with is returning the two separate string values and in general the whole split() function. Any pointers or help is appreciated. Thank you
Instead of a function, consider making Split a procedure having two out parameters, as you've shown. Then decide if Pos is the last index of Part1 or the first index of Part2; I've chosen the latter.
procedure Split(
TheString : in String; Pos : in Integer;
Part1 : out String; Part2 : out String) is
begin
Move(TheString(TheString'First .. Pos - 1), Part1);
Move(TheString(Pos .. TheString'Last), Part2);
end Split;
Note that String indexes are Positive:
type String is array(Positive range <>) of Character;
subtype Positive is Integer range 1 .. Integer'Last;
Doing this is so trivial, I'm not sure why you'd bother making a routine for it. Just about any routine you could come up with is going to be much harder to use anyway.
Front_Half : constant String := Original(Original'first..Index);
Back_Half : constant String := Original(Index+1..Original'last);
Done.
Note that static Ada strings are very different than strings in other languages like C or Java. Due to their static nature, they are best built either inline like I've done above, or as return values from functions. Since functions cannot return more than one value, a single unified "split" routine is just plain not a good fit for static Ada string handling. Instead, you should either do what I did above, call the corresponding routines from Ada.Strings.Fixed (Head and Tail), or switch to using Ada.Strings.Unbounded.Unbounded_String instead of String.
The latter is probably the easiest option, if you want to keep your Java mindset about string handling. If you want to really learn Ada though, I'd highly suggest you learn to deal with static fixed Strings the Ada way.
From looking over your code you really need to read up in general on the String type, because you're dragging in a lot of expectations in from other languages on how to work with them--which aren't going to work with them. Ada's String type is not one of its more flexible features, in that they are always fixed length. While there are ways of working around the limitations in a situation such as you're describing, it would be much easier to simply use Unbounded_Strings.
The input String to your function could remain of type String, which will adjust to the length of the string that you provide to it. The two output Unbounded_Strings then are simply set to the sliced string components after invoking To_Unbounded_String() on each of them.
Given the constraints of your main program, with all strings bounded by the size of EMPTY_STRING. the procedure with out parameters is the correct approach, with the out parameter storage allocated by the caller (on the stack as it happens)
That is not always the case, so it is worth knowing another way. The problem is how to deal with data whose size is unknown until runtime.
Some languages can only offer runtime allocation on the heap (via "new" or "malloc") and can only access the data via pointers, leaving a variety of messy problems including accesses off the end of the data (buffer overruns) or releasing the storage correctly (memory leaks, accessing freed pointers etc)
Ada will allow this method too, but it is usually unnecessary and strongly discouraged. Unbounded_String is a wrapper over this method, while Bounded_String avoids heap allocation where you can accept an upper bound on the string length.
But also, Ada allows variable sized data structures to be created on the stack; the technique just involves creating a new stack frame and declaring new variables where you need to, with "declare". The new variables can be initialised with function calls.
Each function can only return one object, but that object's size can be determined at runtime. So either "Split" can be implemented as 2 functions, returning Part1 or Part2, or it can return a record containing both strings. It would be a record with two size discriminants, so I have chosen the simpler option here. The function results are usually built in place (avoids copying).
The flow in your example would require two nested Declare blocks; if "Pos" could be identified first, they could be collapsed into one...
procedure Split is
function StringBefore( Input : String; Pos : Natural) return String is
begin
return Input(1 .. Pos-1);
end StringBefore;
function StringFrom ...
begin
Put("To split a string, enter the string: ");
declare
AString : String := Get_Line;
Pos : Natural;
begin
Put("Enter the split position: ");
Get(Pos);
declare
Part1 : String := StringBefore(AString, Pos);
Part2 : String := StringFrom(AString, Pos);
begin
Put("The first part is ");
Put_Line(Part1);
Put(" and the second part is ");
Put_Line(Part2);
end; -- Part1 and Part2 are now out of scope
end; -- AString is now out of scope
end Split;
This can obviously be wrapped in a loop, with different size strings each time, with no memory management issues.
Look at the Head and Tail functions in Ada.Strings.Fixed.
function Head (Source : in String; Count : in Natural; Pad : in Character := Space) return String;
function Tail (Source : in String; Count : in Natural; Pad : in Character := Space)
return String;
Here's an approach that just uses slices of the string.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
procedure Main is
str : String := "one,two,three,four,five,six,seven,eight";
pattern : String := ",";
idx, b_idx : Integer;
begin
b_idx := 1;
for i in 1..Ada.Strings.Fixed.Count ( Source => str, Pattern => pattern ) loop
idx := Ada.Strings.Fixed.Index( Source => str(b_idx..str'Last), Pattern => pattern);
Put_Line(str(b_idx..idx-1)); -- process string slice in any way
b_idx := idx + pattern'Length;
end loop;
-- process last string
Put_Line(str(b_idx..str'Last));
end Main;

Ada string comparison

I am new to Ada and currently trying to write a simple program involving an if-else if statement. The code is as follows:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Year_Codes is
Year : String(1..9) := " ";
CharsRead : Natural;
function YearCode(Name : in String) return Integer is
begin
if(Name = "freshman")then
return 1;
elsif(Name = "sophomore")then
return 2;
elsif(Name = "junior")then
return 3;
elsif(Name = "senior")then
return 4;
else
return 0;
end if;
end YearCode;
begin
Put("Enter your academic year: "); -- Prompt for input
Get_Line(Year, CharsRead); -- Input
Put( YearCode(Year) ); -- Convert and output
New_Line;
end Year_Codes;
I am getting 0 for every answer. Any input on what I am doing wrong?
The "=" operation on strings compares the entire strings. If the user's input is "freshman", the value of Name will be "freshman ", not "freshman". Read the documentation for the Get_Line procedure.
You should probably pass YearCode a slice of the Year string, not the entire string; CharsRead tells you what that slice should be.
Specifically, the call should be:
Put( YearCode(Year(Year'First..CharsRead)) );
Here's a case-insensitive version using attributes:
function YearCode(Name : in String) return Integer is
Type Class is (Freshman, Sophmore, Junior, Senior);
begin
Return 1 + Class'Pos(Class'Value(Name));
exception
When CONSTRAINT_ERROR => Return 0;
end YearCode;
With that extra character in your buffer, it looks to me like you are thinking of strings in C terms. You need to stop that. Of everything in the language, string handling is the most different between Ada and C.
While C strings are null terminated, Ada strings are not. Instead, an Ada string is assumed to be the size of the string array object. Its a simple difference, but it has enormous consequences in how you handle strings.
I go into this a bit in my answer to How to I build a string from other strings in Ada? The basic gist is that in Ada you always try to build perfectly-sized string objects on the fly.
Sadly, Text_IO input is one place that has traditionally made that really hard, due to its string buffer-based input. In that case, you are forced to use an overly large string object as a buffer, and use the returned value as the end of the defined area of the buffer, as Keith showed.
However, if you have a new version of the compiler, you can use the function version of Get_Line to fix that. Simply change your middle two lines to:
Put( YearCode(Get_Line) );

Function with PWideChar parameter taking only first char

I've just encountered a weird problem. I am trying to load a model to OpenGL and in the part where I load textures I use auxDIBImageLoadA(dibfile:PWideChar) function. Here is my code calling it
procedure CreateTexture(var textureArray: array of UINT; strFileName: string; textureID: integer); // Vytvožení textury
var
pBitmap: PTAUX_RGBImageRec;
begin
if strFileName = '' then exit;
MessageBox(0,PWideChar(strFileName),nil,SW_SHOWNORMAL);
pBitmap := auxDIBImageLoadA(PWideChar(strFileName));
if pBitmap = nil then exit;
...
The MessageBox is just for control. This is what happens: I run the application, a box with "FACE.BMP" appears. Okay. But then I get an error saying "Failed to open DIB file F". When i set the stFileName to xFACE.BMP, I get an "Failed to open DIB file x". So for some reason it appears that the function is taking only the first char.
Am I missing something? I'm using glaux.dll which I downloaded like 5 times from different sources, so it should be bug-free (I hope, every OpenGL site referred to it).
That's odd, functions ending in "A" generally take PAnsiChar pointers, and those ending in "W" take PWideChar pointers. Is there a auxDIBImageLoadW call also? If there is use that one, or try with PAnsiChar, since the PWideChar you pass (two bytes per position) would look like a string one character long if it is evaluated as a 1-byte string.
You need to convert your Unicode string to ANSI. Do it like this
pBitmap := auxDIBImageLoadA (PAnsiChar(AnsiString(strFileName)))
You would be better off calling the Unicode version though
pBitmap := auxDIBImageLoadW (PWideChar(strFileName))

delphi string to "const buffer"

I have a dll which procedure accepts: "const buffer" parameter.
There is also an example of passing value to this parameter:
var
str: array [0..200 - 1] of Char;
LTextSend: string;
begin
LTextSend := 'Text';
StrLCopy(PChar(#str[0]), PChar(LTextSend), High(str));
Dll_procedure(str, Length(LTextSend));
end;
can this parameter be only passed through this declaration: str: array [0..200 - 1] of Char;
Isn't there an easier way to pass string (of different length) to this dll parameter?
Thanks
You can call the procedure like this:
DLL_procedure(str[1], Length(str));
This works because Delphi untyped parameters are sent the same way as var parameters, they're sent by reference. The compiler will send a pointer to str[1], the first char in the string, but that's actually a pointer to the whole string. As usual, make sure the string actually contains an first character (ie: it's not empty).
Your number 1 problem depends entirely on the DLL. The "signature" allows any length of data to be sent, but that doesn't mean the DLL is prepared for anything. Maybe it can only handle 200 bytes at a time.
I don't know why you're making a copy to a temporary buffer.
It does only make sense if the Dll_procedure will modify the buffer. But I doubt it will.
So you could simply write:
Dll_procedure(Pointer(LTextSend)^, Length(LTextSend));
Or a variant making the string unique before the call:
Dll_procedure(LTextSend[1], Length(LTextSend));

Resources