Lazarus - parse function based on delimiter - lazarus

I am building a small app in Lazarus and need a parse function based on the underscore. For example:
array := Split(string, delimiter);
So string = "this_is_the_first_post" and delimiter is the underscore resulting in the array being returned as:
array[0] = this
array[1] = is
array[2] = the
array[3] = first
array[4] = post
Any one has any idea how to go about this? I have tried a few code examples and it always throws an error.
Thanks.

You can use the following code:
var
List1: TStringList;
begin
List1 := TStringList.Create;
try
List1.Delimiter := '_';
List1.DelimitedText := 'this_is_the_first_post';
ShowMessage(List1[0]);
ShowMessage(List1[1]);
ShowMessage(List1[2]);
ShowMessage(List1[3]);
ShowMessage(List1[4]);
finally
List1.Free;
end;
end;
In this example the output will be shown as a set of messages but you get the general idea.

Related

get multiple inputs instead of one

So, I'm new here and I'm new to programming generally. I made this program that I needed for a project (a pascal program using Lazarus) that allows me to get a kind of list generated by replacing * by numbers. What I need is to be able to give it multiple codes to process at once (maximum 10) instead of entering every code at once.
program b;
{$mode objfpc}
{$H+}
uses sysutils;
var
sourcestr: string;
resultstr: string;
n: integer;
begin
writeln('provide a string:');
readln(sourcestr);
for n := 0 to 99 do begin
resultstr := StringReplace(sourcestr, '*', IntToStr(n div 10), []);
resultstr := StringReplace(resultstr, '*', IntToStr(n mod 10), []);
resultStr := resultStr + ':password';
writeln(resultstr);
end;
end.
I hope you could help me with this and thanks in advance.
The code below shows how to replace an arbitrary number of pairs of asterisks by the two substitute characters you are generating.
for n := 0 to 9 do begin
resultstr := sourcestr;
while Pos('*', resultstr) > 0 do begin
stringReplace(resultstr, '*', IntToStr(n div 10), []);
resultstr := StringReplace(resultstr, '*', IntToStr(n mod 10), []);
end;
resultStr := resultStr + ':password';
writeln(resultstr);
end;
It uses the Pos function in a while loop to replace the asterisk pairs. Be aware that the output may not be exactly what you need, because in each generated resultstr you will get the same substitute characters replacing each pair of asterisks, i.e.
with an input of
a ** b ** c
the resultstrs generated will be like
a00b00c
a11b11c
which may not be what you need. If not, changing the code to do what you do need is left as an exercise for the reader, as they say.
Btw, it occurred to me later that maybe you are asking how to input and process several lines'-worth of user input. One way to do that would be to read the lines into a TStringList (see online help) and then process that. Something like:
var
TL : TStringList;
sourcestr : String;
begin
TL := TStringList.Create;
repeat
readln(sourcestr);
if sourcestr <> '' then
TL.Add(sourcestr);
until sourcestr = '';
for i := 0 to TL.Count - 1 do begin
sourcestr := TL[i];
// process sourcestr however you want
end;
TL.Free;
though you could, of course, simply process sourcestr as you go along, in the repeat..until loop.

Pascal procedure pass variable arrays

When I try to pass variable arrays from procedure to the main program, a[1] in the procedure was supposed to be equal to arr[1] in the main program, like this:
a[1] = arr[1]
a[2] = arr[2]
a[3] = arr[3]
a[4] = arr[4]
a[5] = arr[5]
But the program actually acta like this:
a[1] = ''
a[2] = arr[1]
a[3] = arr[2]
a[4] = arr[3]
a[5] = arr[4]
I don't know what's wrong to the code, can someone point out the mistake?
The simplied code, same problem:
var
arr : array[1..5] of string;
i : integer;
procedure test(var a : array of string);
var
i : integer;
begin
a[1] := 'one';
a[2] := 'two';
a[3] := 'three';
a[4] := 'four';
a[5] := 'five';
for i := 1 to 5 do writeln(a[i]);
end;
begin
test(arr);
write('-----');
for i := 1 to 5 do
begin
writeln(arr[i]);
if arr[i] = '' then writeln('NOTHING');
end;
readln
end.
MartynA gave you the hint to look at "open array parameters" in the online help. But it is not necessary to do what he proposes, using ArrayLoBound etc. The declaration of the actual array can have any index range.
I would do this:
program OpenArrayTest;
{$APPTYPE CONSOLE}
var
{ Initialization like this only possible for global variables. }
Arr: array[11..15] of string = ('once', 'doce', 'trece', 'catorce', 'quince');
I: Integer;
procedure ModifyArray(var A: array of string);
var
I: Integer;
begin
for I := Low(A) to High(A) do
A[I] := A[I] + ' <-- ' + IntToStr(I);
end;
procedure ShowArray(const A: array of string);
begin
for I := Low(A) to High(A) do
Writeln(A[I]);
end;
begin
ModifyArray(Arr);
ShowArray(Arr);
Writeln('-----');
ShowArray(['one', 'two', 'three', 'four', 'five', 'six', 'seven']);
Readln;
end.
The output is:
once <-- 0
doce <-- 1
trece <-- 2
catorce <-- 3
quince <-- 4
-----
one
two
three
four
five
six
seven
In other words, use High() and Low() to access the items in the parameter. Do not use any fixed bounds, since the array can have any length. Also note that you can get the number of items in the array using Length(A) or Length(Arr). You can not only pass static arrays, like Arr, you can also pass dynamic arrays, or use an open array constructor (using [ and ]), like I did in the second call to ShowArray().
More about open arrays in my article "Open arrays and array of const".
Judging by your comment, you are in a bit of a muddle.
In short, if you declare a parameter to a procedure to be an "array of", the array is always zero-based, regardless of the structure of the array you pass to it as an argument, as in
test(arr);
Try the code below. You'll find that when it runs, you get a Range-Check error on the line
a[5] := 'five';
That's because although arr has five elements, they are numbered 0..4, so there is no element of arr with index 5.
Although there are other was to declare procedure parameters, if you want to pass arrays to it as arguments, you have to make sure that either you mentally translate the array indexes as you write the code or (better) you declare the arrays you pass to it as zero-based, as I've done.
And, try and make a habit of turning range-checking on. It will catch error you yourself may overlook.
I'm going to leave you to rewrite your test procedure so that it works correctly as an exercise, because I'm "guessing" that what you've posted is some kind of school- or course-work and you should really put some effort into finding out how to correct your error yourself. If you're still stuck after reading this and trying the obvious solution, ask.
Btw, if you are using Delphi, look up "Open array parameters" in the Online Help. That explains the restrictions on using "array of ..." procedure-parameters.
Also btw, Rudy Velthuis says in his answer "But it is not necessary to do what {MartynA] proposes, using ArrayLoBound etc." That is true, it is not necessary but he has missed my point. If you hard-code array bounds, with values like 1 and 5, and then change them later, it is easy to overlook other values that need updating too, like in your for loop. Defining these values as consts is a good habit to get into because it avoids introducing inconsistencies, but more importantly makes sure you think about what you are doing. IME ...
program arrayparam;
const
ArrayLoBound = 0;
ArrayHiBound = 4;
var
arr : array[ArrayLoBound..ArrayHiBound] of string;
i : integer;
{$R+} // Turn range-checking on
procedure test(var a : array of string);
var
i : integer;
begin
a[1] := 'one';
a[2] := 'two';
a[3] := 'three';
a[4] := 'four';
a[5] := 'five';
for i := 1 to 5 do
writeln(a[i]);
end;
begin
test(arr);
writeln('-----');
for i := ArrayLoBound to ArrayHiBound do
begin
writeln(arr[i]);
if arr[i] = '' then
writeln('NOTHING');
end;
readln
end.
All good answers but just to be complete: the asker can get exactly the result asked for.
It could be that accessing them using 1 to 5 is important for the asker's purposes.
Make the changes below and it will print out as originally expected.
type
TArr = array[ 1..5 ] of string;
var
arr : TArr;
procedure test( var a : TArr );
I agree that defaulting to 0 based arrays is simply easier and using the functions low / hi make it bulletproof.
But I can also see that sometimes indexing in your own way could be of use / important.

Lazarus error "External: SIGSEGV" on variable increment?

I got a problem in my Lazarus project: everytime I want to use a function it throws the above error (External: SIGSEGV). I don't know what that means, but some debugging showed me, that this is the code, causing the error:
class function TUtils.AsStringArray(const Strs:TStrings): TStringArray;
var
s:string;
i:integer;
begin
SetLength(Result, Strs.Count);
i := 1;
for s in Strs do
begin
Result[i] := s;
i := i + 1;
end;
end;
And the definitions
TStringArray = array of string;
TUtils = class
public
[...]
class function AsStringArray(const Strs:TStrings): TStringArray; static;
end;
The exception occurs after i := i + 1;. I would be really thankful if you could help me!
Dynamic arrays such as TStringArray = array of string; are zero-based; your code uses it as 1-based and raises access violation.
You should replace i := 1; by i := 0;
To the second Problem, it is because you are accesing to the index i, wich at the start it is 1 that is why you have the problem, the range of the array is determined by "length - 1", so if your length is 1, then your range is 0. So to solve the problem in your for loop you have to put Result[i-1] := s; like this you acces the index you really want.
More of this on http://wiki.freepascal.org/Dynamic_array

Reading record from a text file into array in Pascal

1.this is my code i want to read a record from a text file into array in pascal my program is about making a hotel helper and i already have a text file with the data of the hotel then i should read it from the text file and store it in array .. but i am facing error 103 exit code (file not open).... any help Please . :)
program Hotel1(input,output);
const max =10; MaxFloor =10;
type
Date = record
day :1..31;
month:1..12;
year:integer;
end;
Booking = record
Guest:string[20];
S_Date:date;
E_date:date;
end;
Booking_Mat= array[1..max] of Booking;
History_Booking = record
B_num:integer;
B_Mat:Booking_Mat;
end;
Room = record
Num:integer;
Bed_num:integer;
Price:integer;
Status:Boolean;
H:History_Booking;
end;
Data = record
Ro:Room;
m:integer;
end;
Data_mat= array [1..max] of Data;
Procedure Read_Data(filename:string; var table:Data_mat);
var df:text; i,j :integer;
n,m,num,GN:integer;
Bed_num,Price:integer;
f:text;
s,e:Date;
Gname:string[20];
ok:boolean;
a:Data_mat;
c:char;
Begin
writeln('Reading ',filename,' records into array.... ');
assign(df,filename);
reset(df);
i:=0;
while (not eof) do
begin
i:=i+1;
Read (f,num);
a[i].Ro.num:=num;
Read (f,Bed_num);
a[i].Ro.Bed_num:=Bed_num;
Read (f,Price);
a[i].Ro.Price:=Price;
Read(f,c);
if (c ='Y') then
a[i].Ro.status:= true
else
a[i].Ro.status:= false;
readln;
End; {while eof}
close(df);
End; {Read_Data}
You've declared two variables of type Text, (df and f) in your var block.
You open df with these lines:
assign(df,filename);
reset(df);
You then read from f (which is not the file you opened above) in several lines, such as this one:
Read (f, num);
It's interesting to note that you actually manage to close the file you really opened, even though you never use it in your loop:
close(df);
The solution to all of these issues is to delete the declaration of either f or df, and then fix the compiler errors you get by correcting the code to use the remaining text variable. (Two important lessons here are
Only declare the variables you actually need.
Use the variables you declare.
Your loop is also invalid, because you're using while not eof with no file provided for which to test the end. Your loop should read while not Eof(df) do instead.
It's also much better to follow the typical naming convention of prefixing types with a T. It makes it clear that it's a type and not a variable, and allows you to read the code more easily. For instance, I'd change your definition of Data to TRoomData, and change the other type declarations accordingly. Here's an example - note that TRoomData now has a field (member) named Room of type TRoom:
TRoomData = record
Room: TRoom;
m: Integer;
end;
TRoom is defined as
TRoom = record
Num: Integer;
Bed_num: Integer;
Price: Integer;
Status: Boolean;
H: THistory_Booking;
end;
And so forth. This allows you to write code more clearly:
var
RoomData: TRoomData;
begin
RoomData.Room.Num := 1;
RoomData.Room.Price := 50;
// etc.
end;
With all that being said, your file does not contain text, and therefore you're using the wrong file type by using df: Text in the first place. You should use a File of TRoomData, allowing you to read and write entire records at a time. Here's an example of doing so:
var
DF: File of TRoomData;
RoomData: TRoomData;
i: Integer;
const
DataFileName = 'D:\TempFiles\RoomData.dat';
Writing it:
// Put some data into the record
RoomData.Room.Num := 1;
RoomData.Room.Bed_num := 1;
RoomData.Room.Price := 40;
RoomData.Room.Status := True;
RoomData.Room.H.B_num := 1;
for i := 1 to Max do
begin
RoomData.Room.H.B_Mat[1].Guest := Format('Guest %d', [i]);
RoomData.Room.H.B_Mat[1].S_Date.Year := 2014;
RoomData.Ro.H.B_Mat[1].S_Date.Month := i;
RoomData.Ro.H.B_Mat[1].S_Date.Day := i;
end;
// Write it out to the file
AssignFile(DF, DataFileName);
try
Rewrite(DF);
Write(DF, RoomData);
finally
CloseFile(DF);
end;
Reading it back in:
AssignFile(DF, DataFileName);
try
Reset(DF);
Read(DF, RoomData);
finally
CloseFile(DF);
end;
(Or, better yet: If the version of Pascal you're using supports it, move away from the old file I/O routines and start using TFileStream instead.)
Last but not least, learn to properly format your code. It makes it much easier to debug and maintain, and it's much easier to read when you can follow the execution path clearly.

Reading from text file into list in FreePascal

I have a text file including:
John###198cm###90kg###19age
Tom###120cm###34kg###8age
And I want to read them from file into two lists in FreePascal.
I have tried to use LoadFromFile function, which should make a line into list, but it is not working for me.
This is a variation of your question Reading from file FreePascal.
Here is an example using ReplaceStr() to convert the ### characters into a CR LF pair.
When assigned to the text property of a new list, it will be splitted into items.
Uses
StrUtils;
procedure HandleText;
var
i : Integer;
sSourceList : TStringList;
sExpandedList : TStringList;
begin
sSourceList := TStringList.Create;
sExpandedList := TStringList.Create;
try
sSourceList.LoadFromFile('MySource.txt');
for i := 0 to sSourceList.Count-1 do begin
sExpandedList.Text := ReplaceStr(sSourceList[i],'###',#13#10);
// Do something with your lists
// sExpandedList[0] = 'John' etc ...
end;
finally
sSourceList.Free;
sExpandedList.Free;
end;
end;

Resources