Ok guys, I've been trying to find out every possible mistake i'm making but I give up... I need help! What I'm writing is an app to manage rentals for my job and when the date is past, my app removes the name from 2 text files. I wrote 3 little functions(procedures) to make this work. Here:
This one loads from dates.dat file and remove the line containing the name of the employee.
procedure remDate(emp: String);/// Removes employee from date file
var
pos1, i: integer;
dateList: TStringList;
begin
dateList:=TStringList.Create;
dateList.LoadFromFile('Data\dates.dat');
for i:=0 to dateList.Count-1 do begin
pos1:=AnsiPos(emp, dateList[i]);
if pos1<>0 then begin
dateList.Delete(i);
dateList.SaveToFile('Data\dates.dat');
end;
end;
dateList.Free;
end; //eo remDate
This one removes the line containing the employee name from the perm.dat file.
procedure remPerm(emp: String);/// Removes employee from perm file
var
pos1, i: integer;
permList: TStringList;
begin
permList:=TStringList.Create;
permList.LoadFromFile('Data\perm.dat');
for i:=0 to permList.Count-1 do begin
pos1:=AnsiPos(emp, permList[i]);
if pos1<>0 then begin
permList.Delete(i);
permList.SaveToFile('Data\perm.dat');
end;
end;
permList.Free;
end; //eo remPerm
This one sticks those together. The isDue is a simple function that compares 2 dates and returns a TRUE if date is today or is past.
procedure updatePerms;
var
empList: TStringList;
i: integer;
begin
empList:=TStringList.Create;
empList.LoadFromFile('Data\employes.dat');
for i:=0 to empList.Count-1 do begin
if isDue(empList[i]) then begin
remDate(empList[i]);
remPerm(empList[i]); (*) Here is where the error points.
end;
end;
empList.Free;
end;
The error I get is when it gets to remPerm in the updatePerms procedure.(*)
I get a EStringList Error, out of bound (#). Figured out with many tries that it only happens when an employee's due date is today. Please comment if you need more info!
Thanks in advance, any help is really appreciated!
The problem is that you are using a for loop. The end point of a for loop is only evaluated once when the loop is entered. At that point you may have 100 items, but once you start deleting there will be less. This will then result in a list index out of bounds error.
The simple fix is to reverse the for loop:
procedure remDate(emp: String);
/// Removes employee from date file
var
pos1, i: integer;
dateList: TStringList;
begin
dateList := TStringList.Create;
dateList.LoadFromFile('Data\dates.dat');
for i := dateList.Count - 1 downto 0 do
begin
pos1 := AnsiPos(emp, dateList[i]);
if pos1 <> 0 then
begin
dateList.Delete(i);
dateList.SaveToFile('Data\dates.dat');
end;
end;
dateList.Free;
end; // eo remDate
This will work if the employee occurs more than once.
However if the employee does only occur once, you can use break to exit from the loop early:
procedure remDate(emp: String);
/// Removes employee from date file
var
pos1, i: integer;
dateList: TStringList;
begin
dateList := TStringList.Create;
dateList.LoadFromFile('Data\dates.dat');
for i := 0 to dateList.Count - 1 do
begin
pos1 := AnsiPos(emp, dateList[i]);
if pos1 <> 0 then
begin
dateList.Delete(i);
dateList.SaveToFile('Data\dates.dat');
Break; // <-- early exit
end;
end;
dateList.Free;
end; // eo remDate
Another solution is to use a while loop.
Related
I'd like to delete checked items from CheckListBox by cliicking TButton but I've found only how to delete selected item and it's not what I'm looking for. I hope you can help me
This code will do what you want. Note that CheckBoxList1.Items.Count is decreasing by one every time an item is deleted. For this reason we're using downto instead of do in the for-loop.
procedure TForm1.Button1Click(Sender: TObject);
var
index: Integer;
begin
CheckListBox1.Items.BeginUpdate;
try
for index := (CheckListBox1.Items.Count - 1) downto 0 do
if (CheckListBox1.Checked[index]) then
CheckListBox1.Items.Delete(index);
finally
CheckListBox1.Items.EndUpdate;
end;
end;
The CheckListBox1.Items.BeginUpdate; and CheckListBox1.Items.EndUpdate; statements ensure that the control doesn't repaint itself while we're processing its items.
if i understand what you need, try following code:
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
count : Integer;
begin
i:= 0;
count := CheckListBox1.Items.Count - 1;
while i <= count do
if CheckListBox1.Checked[i] = true then
begin
CheckListBox1.Items.Delete(i);
count := count - 1;
end else i:=i+1;
end;
Of curse, there are better solutions, but it is ready to use for you.
procedure TfrmSongs.Display;
var
i: Integer;
begin
redOutput.Clear;
redOutput.Lines.Add('The TOP 10');
for i := 1 to iCount-1 do
begin
redOutput.Lines.Add(IntToStr(i)+arrSongs[i]);
end;
end;
procedure TfrmSongs.FormActivate(Sender: TObject);
var
tSongList: TextFile;
sSong: string;
begin
iCount := 0;
AssignFile(tSongList, ExtractFilePath(Application.ExeName)+'Songs.txt');
Reset(tSongList);
while not EOF do
begin
Readln(tSongList, sSong);
arrSongs[iCount] := sSong;
Inc(iCount);
end;
CloseFile(tSongList);
Display;
end;
I'm trying to display the array I tried to create via a text file in a rich edit. But every time I run the app, it gives me an 'I/O error 6' error and nothing displays. I don't know if it's something with the text file or if it's something with the display procedure.
There are a few problems with your code, but regarding the I/O error specifically, error 6 means "invalid file handle".
Since you are getting a popup error notification, you clearly have I/O checking enabled, which it is by default.
I/O error 6 is not typical for a failure on System.Reset(), and you are not seeing any other kind of error related to a failure in opening a file, so we can safely assume that the file is being opened successfully, and that System.Readln() and System.CloseFile() are not being passed an invalid I/O handle.
So that leaves just one line that could be receiving an invalid I/O handle:
while not EOF do
System.Eof() has an optional parameter to tell it which file to check. Since you are omitting that parameter, Eof() will use System.Input instead. And a GUI process does not have a STDIN handle assigned by default. So that is likely where error 6 is coming from.
That line needs to be changed to this instead:
while not EOF(tSongFile) do
UPDATE: given the declaration of arrSongs you have shown in comments (arrSongs: array[1..MAX] of string;), there are additional problems with your code. You need to make sure the reading loop does not try to store more than MAX strings in the array. Also, your reading loop is trying to store a string at index 0, which is not a valid index since the array starts at index 1. Also, Display() is skipping the last string in the array. See what happens when you omit important details?
Try this instead:
private
arrSongs: array[1..MAX] of string;
...
procedure TfrmSongs.Display;
var
i: Integer;
begin
redOutput.Clear;
redOutput.Lines.Add('The TOP 10');
for i := 1 to iCount do
begin
redOutput.Lines.Add(IntToStr(i) + arrSongs[i]);
end;
end;
procedure TfrmSongs.FormActivate(Sender: TObject);
var
tSongList: TextFile;
sSong: string;
begin
iCount := 0;
AssignFile(tSongList, ExtractFilePath(Application.ExeName) + 'Songs.txt');
Reset(tSongList);
try
while (not EOF(tSongList)) and (iCount < MAX) do
begin
Readln(tSongList, sSong);
arrSongs[1+iCount] := sSong;
Inc(iCount);
end;
finally
CloseFile(tSongList);
end;
Display;
end;
That being said, I would suggest getting rid of the reading loop completely. You can use a TStringList instead:
uses
..., System.Classes;
...
private
lstSongs: TStringList;
...
procedure TfrmSongs.Display;
var
i: Integer;
begin
redOutput.Clear;
redOutput.Lines.Add('The TOP 10');
for i := 0 to lstSongs.Count-1 do
begin
redOutput.Lines.Add(IntToStr(i+1) + lstSongs[i]);
end;
end;
procedure TfrmSongs.FormCreate(Sender: TObject);
begin
lstSongs := TStringList.Create;
end;
procedure TfrmSongs.FormDestroy(Sender: TObject);
begin
lstSongs.Free;
end;
procedure TfrmSongs.FormActivate(Sender: TObject);
begin
lstSongs.LoadFromFile(ExtractFilePath(Application.ExeName) + 'Songs.txt');
Display;
end;
Or, you can use TFile.ReadAllLines() instead:
uses
..., System.IOUtils;
...
private
arrSongs: TStringDynArray;
...
procedure TfrmSongs.Display;
var
i: Integer;
begin
redOutput.Clear;
redOutput.Lines.Add('The TOP 10');
for i := 0 to High(arrSongs) do
begin
redOutput.Lines.Add(IntToStr(i+1) + arrSongs[i]);
end;
end;
procedure TfrmSongs.FormActivate(Sender: TObject);
begin
arrSongs := TFile.ReadAllLines(ExtractFilePath(Application.ExeName) + 'Songs.txt');
Display;
end;
I don't know Delphi and would someone help me to change a small Delhpi script from vmprotect?
function GetRandomSectionName: String;
var I:Integer;
B:Byte;
begin
Result:='';
for I:=1 to 8 do
begin
B:=32+Random(Ord('z')-32);
Result:=Result+Chr(B);
end;
end;
procedure OnAfterSaveFile;
var I:Integer;
begin
with VMProtector.OutputFile do
for I:=0 to Sections.Count-1 do
Sections.Items[I].Name:=GetRandomSectionName;
end;
It should only randomize the sections which is starting with .vmp the rest should stay.
It should only randomize the sections which is starting with .vmp the rest should stay.
Use the System.Pos function to match a substring in a string.
procedure OnAfterSaveFile;
var I:Integer;
begin
with VMProtector.OutputFile do
for I:=0 to Sections.Count-1 do
if Pos('.vmp',Sections.Items[I].Name) = 1 then // Only .vmp sections
Sections.Items[I].Name:=GetRandomSectionName;
end;
Ok here's the thing, my program runs perfect.. no errors at all until I add another button onto my form.
I have a stringgrid in parallel with an array[1..200].
I have a delete button:
Procedure TForm1.DeleteTeam(TeamName: string);
var
i : integer;
begin
for i := 1 to TotalNumberOfTeams do
begin
if TeamName = SailingTeams[i].TeamName then
begin
sailingTeams[i].AvLapTime := 99999999;
//puts one to delete with largest value to delete
SortArray;
//sorts it so it is at the end of array
sailingTeams[TotalNumberOfTeams] := nil;
TotalNumberOfTeams := TotalNumberOfTeams - 1;
sortArray;
UpdateGrid;
Break;
end;
end;
end;
procedure TForm1.btnDeleteClick(Sender: TObject);
var
TeamName : string;
continue : integer;
begin
TeamName := (strGrid.Cells[0,(strGrid.Row)]);
continue := MessageDlg('Are you sure you wish to delete: '+teamName+'?',mtWarning, mbYESNO, 0);
if continue = mrYES then
DeleteTeam(TeamName);
end;
If I add a button and click it, then click delete I get the error :S :S
Just a home project for a 24 hour dingy race so efficiency of algorithms and stuff not important but any ideas as to why I'm getting this error?? :S
i have a problem on how to retrieve the items in memo from table into tchecklistbox...before this i do the procedure to insert the items that have been checked into memo in tbl POS_CatBreakDownValue..but now i want to know how to retrieve items from table and automatic check in tchecklistbox...thanks..here my procedure to insert the checklist into memo in table..i hope anyone can help me..thanks
procedure TfrmSysConfig.saveCatBreakDownValue;
var
lstCat:TStringList;
i :integer;
begin
lstcat := TStringList.Create;
try
for i:=0 to clCat.Items.Count-1 do begin
if clCat.Checked[i] then begin
lstcat.Add(clcat.Items.Strings[i]);
end;
end;
tblMainPOS_CatBreakDownValue.Value := lstCat.Text;
finally
lstcat.Free;
end;
end;
procedure TfrmSysConfig.saveCatBreakDownValue;
var
lstCat: TStringList;
i:integer;
begin
lstcat := TStringList.Create;
try
for i:=0 to clCat.Items.Count-1 do begin
if clCat.Checked[i] then begin
lstcat.Add(clcat.Items.Strings[i]);
end;
end;
tblMainPOS_CatBreakDownValue.Value := lstCat.Text;
finally
lstcat.Free;
end;
end;
Reading your code I'm guessing you've got a MemoField in a database that you are reading and writing the checked values from/to. You also have a predefined list of checkable items.
So you'll need to create a new string list and read the field back into it (Revesing the writing code). for each item in the list get the Index and check it.
Something like..
procedure TfrmSysConfig.saveCatBreakDownValue;
var
lstCat: TStringList;
i, Index:integer;
begin
lstcat := TStringList.Create;
try
lstcat.Text = tblMainPOS_CatBreakDownValue.Value;
for i:=0 to lstcat.Count-1 do
begin
Index := clCat.Items.IndexOf(lstcat.Items[i])
if Index > -1 then
begin
clCat.Checked[Index] := True;
end;
end;
finally
lstcat.Free;
end;
end;
Not sure I understand your question 100%
TCheckListBox can check or uncheck items using the Checked Property.
CheckListBox.Checked[Index] := True/False;
Since you sound like your comparing strings you may need to determine the index in the TCheckListBox based on the string this can be done like this:
CheckListBox1.Items.IndexOf('StringToFind')
If the string is not found then the result is -1;
If you want to check lines in a TMemo Control and see if they exists as rows in a table you can do the following.
While not Table.EOF do
begin
if Memo1.lines.IndexOf(Table.FieldByName('MyField').AsString) = -1 then
begin
// What you want to do if not found
end
else
begin
// what you want to do if it is found.
end;
Table.Next;
end;