I am working on a Question/Answer UI application in Pascal / Lazarus. My problem is that upon invoking below code through a button click, the program crashes with a Segmentation Fault error.
// more declarations... (UI Form, Buttons, ...)
type
TQuestion = class(TObject)
title: string;
answers: array of string;
correct: integer;
end;
var
questions: array of TQuestion;
procedure TForm1.BStartClick(Sender: TObject);
var
i: integer;
j: integer;
line: string;
arrayLength: integer;
question: TQuestion;
stringList: TStringList;
begin
stringList := TStringList.create;
stringList.LoadFromFile('questions.txt');
for i := 0 to stringList.Count - 1 do ;
begin
line := stringList[i];
if (length(line) >= 2) then
if (line[2] = ' ') and ((line[1] = '-') or (line[1] = '+')) then
begin
arrayLength := length(question.answers);
SetLength(question.answers, arrayLength + 1);
question.answers[arrayLength] :=
Copy(line, 2, Length(line) - 1);
if zeile[1] = '+' then
question.correct := arrayLength;
end
else
begin
question := TQuestion.Create;
question.title := line;
arrayLength := length(questions);
setLength(questions, arrayLength + 1);
questions[arrayLength] := question;
end;
end;
BStart.Visible := False;
end;
Well, my Pascal knowledge goes to 10 to 15 years ago. However, I can see that you have an extra semicolon at the end of this line:
for i := 0 to stringList.Count - 1 do ;
I have these data:
CMD210 STA_ 99.0 uS Temp 22.1 C
CMD210 STAB 99.9 uS Temp 22 C
CMD210 STAB 0.1 mS Temp 22.1 C
CMD210 STA_ 99.5 uS Temp 22.1 C
CMD210 STAB 99.4 uS Temp 22 C
CMD210 ST__ 99.0 uS Temp 22.2 C
CMD210 STAB 0.1 mS Temp 22 C
CMD210 STAB 99.3 uS Temp 22.2 C
I would like to have a program that display the temperature from memo for exampel in a listbox.
I know I have to get loop and something with 2 char with 'p' and 'c', because the number is between those to letters....
procedure TForm1.Button4Click(Sender: TObject);
var
midlet,midler:char;
resultat,x:integer;
linecount,index:integer;
found: boolean;
begin
midlet:= 'p';
midler:='C';
index:=0;
resultat:=midlet+x+midler
found := false;
linecount := Memo1.lines.count;
while index<= linecount - 1 do
begin
if x = memo1.lines[index] then
found := true;
index :=index + 1;
end
if found = true then
ListBox1.text:= floattostrF(x,ffFixed,15,2);
end;
There are several problems in your example so this answer will be limited to "how extracting and converting the temperature from a line". You have fundamentally two ways to achieve the task:
use the regular expressions.
write a custom parser.
the custom parser is quite easy to write:
accumulate non-blank chars in an identifier.
if the identifier is equal to Temp then define a flag.
convert the identifier to a double if the flag is defined and if someting's been accumulated.
example:
program Project1;
uses
sysutils;
const
line1 = 'CMD210 STAB 99.3 uS Temp 22.2 C';
line2 = 'CMD210 STAB 0.1 mS Temp 22 C';
line3 = 'it is quite hot over there Temp 55.123456 C';
line4 = 'bla bla bla bla 12.564 C';
line5 = '';
function getTemperature(aLine: string): double;
var
reader: PChar;
identifier: string;
AccumulateTemp: boolean;
const
_Nan = 0/0;
begin
// initialize local variables.
identifier := '';
AccumulateTemp := false;
// success can be tested with isNan()
result := _Nan;
// add a distinct terminal char:
aLine := aLine + #0;
reader := #aLine[1];
while(true) do begin
if reader^= #0 then
exit;
// blank: test the identifier
if reader^ in [#9, ' '] then
begin
if AccumulateTemp then
begin
if not TryStrToFloat(identifier, result) then
result := _Nan;
AccumulateTemp := false;
exit;
end;
if identifier = 'Temp' then
AccumulateTemp := true;
identifier := '';
end else
// accumulate
identifier := identifier + reader^;
Inc(reader);
end;
end;
begin
DecimalSeparator := '.';
writeln( format('%.7f', [getTemperature(line1)]) );
writeln( format('%.7f', [getTemperature(line2)]) );
writeln( format('%.7f', [getTemperature(line3)]) );
writeln( format('%.7f', [getTemperature(line4)]) );
writeln( format('%.7f', [getTemperature(line5)]) );
readln;
end.
which outputs
22.2000000
22.0000000
55.1234560
Nan
Nan
I'm using Ubuntu 11.04 and Free Pascal Compiler version 2.4.0-2ubuntu3.1 [2011/06/17] for x86_64 Copyright (c) 1993-2009 by Florian Klaempfl
when I'm trying to compile this code:
program test;
uses AVL_Tree;
const N = 10;
type Number = ^Integer;
var all : TAVLTree;
actual :TAVLTreeNode;
a,i :Integer;
t : array[1..N] of Integer;
x :Number;
function compare(a,b:Pointer):LongInt;
begin
compare := Number(a)^ - Number(b)^;
end;
begin
randomize;
for i := 1 to N do
t[i] := i;
all := TAVLTree.Create(#compare);
for i := N downto 1 do
begin
a:=1+random(i);
new(x);
x^:=t[a];
all.add(x);
t[a]:=t[i];
end;
actual := all.findLowest;
while actual <> nil do
begin
Writeln(Number(actual.data)^);
dispose(Number(actual.data));
actual := all.FindSuccessor(actual);
end;
all.Free;
end.
it gives me:
Compiling avl.pas
Fatal: Can't find unit AVL_Tree used by test
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode (normal if you did not specify a source file to be compiled)
I don't know what is going on.. in my school this code worked fine.. Any ideas what should I do?
I was trying to speed up a certain routine in an application, and my profiler, AQTime, identified one method in particular as a bottleneck. The method has been with us for years, and is part of a "misc"-unit:
function cwLeftPad(aString:string; aCharCount:integer; aChar:char): string;
var
i,vLength:integer;
begin
Result := aString;
vLength := Length(aString);
for I := (vLength + 1) to aCharCount do
Result := aChar + Result;
end;
In the part of the program that I'm optimizing at the moment the method was called ~35k times, and it took a stunning 56% of the execution time!
It's easy to see that it's a horrible way to left-pad a string, so I replaced it with
function cwLeftPad(const aString:string; aCharCount:integer; aChar:char): string;
begin
Result := StringOfChar(aChar, aCharCount-length(aString))+aString;
end;
which gave a significant boost. Total running time went from 10,2 sec to 5,4 sec. Awesome! But, cwLeftPad still accounts for about 13% of the total running time. Is there an easy way to optimize this method further?
Your new function involves three strings, the input, the result from StringOfChar, and the function result. One of them gets destroyed when your function returns. You could do it in two, with nothing getting destroyed or re-allocated.
Allocate a string of the total required length.
Fill the first portion of it with your padding character.
Fill the rest of it with the input string.
Here's an example:
function cwLeftPad(const aString: AnsiString; aCharCount: Integer; aChar: AnsiChar): AnsiString;
var
PadCount: Integer;
begin
PadCount := ACharCount - Length(AString);
if PadCount > 0 then begin
SetLength(Result, ACharCount);
FillChar(Result[1], PadCount, AChar);
Move(AString[1], Result[PadCount + 1], Length(AString));
end else
Result := AString;
end;
I don't know whether Delphi 2009 and later provide a double-byte Char-based equivalent of FillChar, and if they do, I don't know what it's called, so I have changed the signature of the function to explicitly use AnsiString. If you need WideString or UnicodeString, you'll have to find the FillChar replacement that handles two-byte characters. (FillChar has a confusing name as of Delphi 2009 since it doesn't handle full-sized Char values.)
Another thing to consider is whether you really need to call that function so often in the first place. The fastest code is the code that never runs.
Another thought - if this is Delphi 2009 or 2010, disable "String format checking" in Project, Options, Delphi Compiler, Compiling, Code Generation.
StringOfChar is very fast and I doubt you can improve this code a lot. Still, try this one, maybe it's faster:
function cwLeftPad(aString:string; aCharCount:integer; aChar:char): string;
var
i,vLength:integer;
origSize: integer;
begin
Result := aString;
origSize := Length(Result);
if aCharCount <= origSize then
Exit;
SetLength(Result, aCharCount);
Move(Result[1], Result[aCharCount-origSize+1], origSize * SizeOf(char));
for i := 1 to aCharCount - origSize do
Result[i] := aChar;
end;
EDIT: I did some testing and my function is slower than your improved cwLeftPad. But I found something else - there's no way your CPU needs 5 seconds to execute 35k cwLeftPad functions except if you're running on PC XT or formatting gigabyte strings.
I tested with this simple code
for i := 1 to 35000 do begin
a := 'abcd1234';
b := cwLeftPad(a, 73, '.');
end;
and I got 255 milliseconds for your original cwLeftPad, 8 milliseconds for your improved cwLeftPad and 16 milliseconds for my version.
You call StringOfChar every time now. Of course this method checks if it has something to do and jumps out if length is small enough, but maybe the call to StringOfChar is time consuming, because internally it does another call before jumping out.
So my first idea would be to jump out by myself if there is nothing to do:
function cwLeftPad(const aString: string; aCharCount: Integer; aChar: Char;): string;
var
l_restLength: Integer;
begin
Result := aString;
l_restLength := aCharCount - Length(aString);
if (l_restLength < 1) then
exit;
Result := StringOfChar(aChar, l_restLength) + aString;
end;
You can speed up this routine even more by using lookup array.
Of course it depends on your requirements. If you don't mind wasting some memory...
I guess that the function is called 35 k times but it has not 35000 different padding lengths and many different chars.
So if you know (or you are able to estimate in some quick way) the range of paddings and the padding chars you could build an two-dimensional array which include those parameters.
For the sake of simplicity I assume that you have 10 different padding lengths and you are padding with one character - '.', so in example it will be one-dimensional array.
You implement it like this:
type
TPaddingArray = array of String;
var
PaddingArray: TPaddingArray;
TestString: String;
function cwLeftPad4(const aString:string; const aCharCount:integer; const aChar:char; var anArray: TPaddingArray ): string;
begin
Result := anArray[aCharCount-length(aString)] + aString;
end;
begin
//fill up the array
SetLength(StrArray, 10);
PaddingArray[0] := '';
PaddingArray[1] := '.';
PaddingArray[2] := '..';
PaddingArray[3] := '...';
PaddingArray[4] := '....';
PaddingArray[5] := '.....';
PaddingArray[6] := '......';
PaddingArray[7] := '.......';
PaddingArray[8] := '........';
PaddingArray[9] := '.........';
//and you call it..
TestString := cwLeftPad4('Some string', 20, '.', PaddingArray);
end;
Here are benchmark results:
Time1 - oryginal cwLeftPad : 27,0043604142394 ms.
Time2 - your modyfication cwLeftPad : 9,25971967336897 ms.
Time3 - Rob Kennedy's version : 7,64538131122457 ms.
Time4 - cwLeftPad4 : 6,6417059620664 ms.
Updated benchmarks:
Time1 - oryginal cwLeftPad : 26,8360194218451 ms.
Time2 - your modyfication cwLeftPad : 9,69653117046119 ms.
Time3 - Rob Kennedy's version : 7,71149259179622 ms.
Time4 - cwLeftPad4 : 6,58248533610693 ms.
Time5 - JosephStyons's version : 8,76641780969192 ms.
The question is: is it worth the hassle?;-)
It's possible that it may be quicker to use StringOfChar to allocate an entirely new string the length of string and padding and then use move to copy the existing text over the back of it.
My thinking is that you create two new strings above (one with FillChar and one with the plus). This requires two memory allocates and constructions of the string pseudo-object. This will be slow. It may be quicker to waste a few CPU cycles doing some redundant filling to avoid the extra memory operations.
It may be even quicker if you allocated the memory space then did a FillChar and a Move, but the extra fn call may slow that down.
These things are often trial-and-error!
You can get dramatically better performance if you pre-allocate the string.
function cwLeftPadMine
{$IFDEF VER210} //delphi 2010
(aString: ansistring; aCharCount: integer; aChar: ansichar): ansistring;
{$ELSE}
(aString: string; aCharCount: integer; aChar: char): string;
{$ENDIF}
var
i,n,padCount: integer;
begin
padCount := aCharCount - Length(aString);
if padCount > 0 then begin
//go ahead and set Result to what it's final length will be
SetLength(Result,aCharCount);
//pre-fill with our pad character
FillChar(Result[1],aCharCount,aChar);
//begin after the padding should stop, and restore the original to the end
n := 1;
for i := padCount+1 to aCharCount do begin
Result[i] := aString[n];
end;
end
else begin
Result := aString;
end;
end;
And here is a template that is useful for doing comparisons:
procedure TForm1.btnPadTestClick(Sender: TObject);
const
c_EvalCount = 5000; //how many times will we run the test?
c_PadHowMany = 1000; //how many characters will we pad
c_PadChar = 'x'; //what is our pad character?
var
startTime, endTime, freq: Int64;
i: integer;
secondsTaken: double;
padIt: string;
begin
//store the input locally
padIt := edtPadInput.Text;
//display the results on the screen for reference
//(but we aren't testing performance, yet)
edtPadOutput.Text := cwLeftPad(padIt,c_PadHowMany,c_PadChar);
//get the frequency interval of the OS timer
QueryPerformanceFrequency(freq);
//get the time before our test begins
QueryPerformanceCounter(startTime);
//repeat the test as many times as we like
for i := 0 to c_EvalCount - 1 do begin
cwLeftPad(padIt,c_PadHowMany,c_PadChar);
end;
//get the time after the tests are done
QueryPerformanceCounter(endTime);
//translate internal time to # of seconds and display evals / second
secondsTaken := (endTime - startTime) / freq;
if secondsTaken > 0 then begin
ShowMessage('Eval/sec = ' + FormatFloat('#,###,###,###,##0',
(c_EvalCount/secondsTaken)));
end
else begin
ShowMessage('No time has passed');
end;
end;
Using that benchmark template, I get the following results:
The original: 5,000 / second
Your first revision: 2.4 million / second
My version: 3.9 million / second
Rob Kennedy's version: 3.9 million / second
This is my solution. I use StringOfChar instead of FillChar because it can handle unicode strings/characters:
function PadLeft(const Str: string; Ch: Char; Count: Integer): string;
begin
if Length(Str) < Count then
begin
Result := StringOfChar(Ch, Count);
Move(Str[1], Result[Count - Length(Str) + 1], Length(Str) * SizeOf(Char));
end
else Result := Str;
end;
function PadRight(const Str: string; Ch: Char; Count: Integer): string;
begin
if Length(Str) < Count then
begin
Result := StringOfChar(Ch, Count);
Move(Str[1], Result[1], Length(Str) * SizeOf(Char));
end
else Result := Str;
end;
It's a bit faster if you store the length of the original string in a variable:
function PadLeft(const Str: string; Ch: Char; Count: Integer): string;
var
Len: Integer;
begin
Len := Length(Str);
if Len < Count then
begin
Result := StringOfChar(Ch, Count);
Move(Str[1], Result[Count - Len + 1], Len * SizeOf(Char));
end
else Result := Str;
end;
function PadRight(const Str: string; Ch: Char; Count: Integer): string;
var
Len: Integer;
begin
Len := Length(Str);
if Len < Count then
begin
Result := StringOfChar(Ch, Count);
Move(Str[1], Result[1], Len * SizeOf(Char));
end
else Result := Str;
end;
I want to develop a setup package for conditionally upgrading an existing package. I want to check the existing software version against to-be-installed version. In order to do that, I have to compare the version strings.
How can I convert the string value to a numerical value in a Inno setup script?
RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Blah blah', 'Version', version)
version = 'V1.R2.12';
numVersion := ??string_to_numerical_value??(version);
This is a little more tricky, as you would want to handle versions like 'V1.R2.12' and 'V0.R15.42' correctly - with the simple conversion in the other answer you would get 1212 and 1542, which would not compare the way you would expect.
You need to decide how big each part of the version number can be, and multiply the parts by that value to get a correct end number. Something like this:
[Code]
function string_to_numerical_value(AString: string; AMaxVersion: LongWord): LongWord;
var
InsidePart: boolean;
NewPart: LongWord;
CharIndex: integer;
c: char;
begin
Result := 0;
InsidePart := FALSE;
// this assumes decimal version numbers !!!
for CharIndex := 1 to Length(AString) do begin
c := AString[CharIndex];
if (c >= '0') and (c <= '9') then begin
// new digit found
if not InsidePart then begin
Result := Result * AMaxVersion + NewPart;
NewPart := 0;
InsidePart := TRUE;
end;
NewPart := NewPart * 10 + Ord(c) - Ord('0');
end else
InsidePart := FALSE;
end;
// if last char was a digit the last part hasn't been added yet
if InsidePart then
Result := Result * AMaxVersion + NewPart;
end;
You can test this with the following code:
function InitializeSetup(): Boolean;
begin
if string_to_numerical_value('V1.R2.12', 1) < string_to_numerical_value('V0.R15.42', 1) then
MsgBox('Version ''V1.R2.12'' is not as recent as version ''V0.R15.42'' (false)', mbConfirmation, MB_OK);
if string_to_numerical_value('V1.R2.12', 100) > string_to_numerical_value('V0.R15.42', 100) then
MsgBox('Version ''V1.R2.12'' is more recent than version ''V0.R15.42'' (true)', mbConfirmation, MB_OK);
Result := FALSE;
end;
Whether you pass 10, 100 or 1000 for AMaxVersion depends on the number and range of your version number parts. Note that you must not overflow the LongWord result variable, which has a maximum value of 2^32 - 1.
I haven't tried that (and my Pascal knowledge is a bit rusty), but something like the following should work:
function NumericVersion(s: String): Integer;
var
i: Integer;
s1: String;
begin
s1 := '';
for i := 0 to Length(s)-1 do
if (s[i] >= '0') and (s[i] <= '9') then
s1 := s1 + s[i];
Result := StrToIntDef(s1, 0);
end;
Please not that you'll have to play with the start and end value for i as I'm not sure whether it is zero-based or not (s[0] may contain the length of the string if it is a "Pascal String").
I've implemented two version strings (actually one string and one dword value) in the registry to overcome complexity.
displayversion="v1.r1.0"
version="10100" (=1*10^4 + 1*10^2 + 0*10^0)
That's simple. Though not an answer to this question, however one might think the other way around when faced with complexity, which could be avoided in a simpler way.