Delphi: Advanced string search commands - delphi

Kenneth is a string. Let's say it contains 'justabcsome123texthaha'.
I know this already:
To find text:
if(pos('bcsome12',Kenneth) > 0) then
To check length:
if(Length('Kenneth') > 10) then
Question 1:
I want to find 'texthaha', but only if it is at the end of the string.
if(pos('texthaha',Kenneth) > 0) then
Sadly this will find it anywhere, even if it is in the middle. Is there a simple way?
Question 2:
Is there a simple way to do a search, but with a * (any character in between)?
For example, if I want to search for bcsome1*3text and I don't care what character the * is. I think it's called a wildcard, isn't it?
if(pos('bcsome1'*'3text',Kenneth) > 0) then
I know the above doesn't work. but is there a similar way?
Edit: Might be of importance: **Delphi version used is very old, not sure of the version, but it's from year 2006.

There are functions EndsStr() and EndsText() (the last is case-insensitive) in the StrUtils unit
But, you easily could provide the needed functionality with known functions (Pos also has overloaded version with the third parameter in fresh Delphi):
NPos = Length(S) - Length(Sub) + 1;
if PosEx(Sub, S, NPos) = NPos then...
or variant proposed by #Sertac Akyuz:
if Copy(S, NPos, Length(Sub)) = Sub ...
The second problem might be solved with function like MatchesMask()
if MatchesMask(Kenneth, '*bcsome1*3text*')...

To get the last occurrence, try LastDelimiter (see help). For wildcards, see this post.

Related

FindFirst and question mark

I need to delete all files, which names are started with "a", then three arbitrary letters and ".txt" extension like "a123.txt". Here is the code:
var
sFileMask: string;
tsrMessage: TSearchRec;
begin
sFileMask := 'c:/a???.txt';
if SysUtils.FindFirst(sFileMask, 0, tsrMessage) = 0 then
begin
repeat
ShowMessage(tsrMessage.Name);
until FindNext(tsrMessage) <> 0;
SysUtils.FindClose(tsrMessage);
end;
end;
I always thought that the question mark means one and only one character, but to my surprise found that this code returns "a.txt", "a1.txt" and "a123.txt" file names. Is there a simple way to modify the code for it to seek only files like "a123.txt"?
The simplest solution for your specific need is to replace this:
ShowMessage(tsrMessage.Name);
with this
if length(tsrMessage.Name)=8 then ShowMessage(tsrMessage.Name);
this will ensure that the length of the file name is exactly four characters + the period + the extension. Like David says, there's no way to have the API do this kind of filtering, so you'll have to do it yourself, but in your particular case, there's no need to enumerate the entire directory. You may at least let the API do the filtering it can do, and then do your own filtering on top of it.
EDIT: If you need to ensure that the three characters following the "a" are digits, you can do it this way:
if (length(tsrMessage.Name)=8) and tsrMessage[2].IsDigit and tsrMessage[3].IsDigit and tsrMessage[4].IsDigit then ShowMessage(tsrMessage.Name);
provided you are using a modern compiler (you'll need to include the "Characters" unit). Also take note that if you are compiling a mobile version, you'll need to use index [1], [2] and [3] instead, as they start index at 0 for strings.
If you are using an older version, you can do it like this:
function IsDigit(c : char) : boolean;
begin
Result:=(c>='0') and (c<='9')
end;
if (length(tsrMessage.Name)=8) and IsDigit(tsrMessage[2]) and IsDigit(tsrMessage[3]) and IsDigit(tsrMessage[4]) then ShowMessage(tsrMessage.Name);
This behaviour is as designed. It is explained by Raymond Chen here: How did wildcards work in MS-DOS?
You will see the exact same behaviour from the command interpreter.
C:\Desktop>dir a???.txt
Volume in drive C has no label.
Volume Serial Number is 20DA-7FEB
Directory of C:\Desktop
26/06/2016 14:03 6 a.txt
26/06/2016 14:03 6 a1.txt
26/06/2016 14:03 6 a12.txt
26/06/2016 14:03 6 a123.txt
4 File(s) 24 bytes
0 Dir(s) 286,381,445,120 bytes free
There is no way to persuade FindFirstFile (the API that is behind the RTL's FindFirst on Windows) to behave the way you wish. Your best option is to enumerate the entire directory, and perform your own filtering using your chosen pattern matching algorithm.

Should parameters be used as variables in Lua?

I've been told in Java that I should avoid modifying the original parameters such as
public int doStuff(int begin, int end) {
/* loop or something */
begin++; //bad
end--; //also bad
/* end loop */
return
}
instead, I should do something like
public int doStuff(int begin, int end) {
int myBegin = begin; //something like this
int myEnd = end;
/* stuff */
return
}
So, I've been doing this in lua
function do_stuff(begin, last)
local my_begin = begin
local my_last = last
--stuff
my_begin = my_begin + 1
my_last = my_last - 1
--stuff
end
But, I'm wondering if
function do_stuff(begin, last)
--stuff
begin = begin + 1
last = last - 1
--stuff
end
is also discouraged, or is it nice and concise?
There are no rules. Let taste, clarity, and need decide.
Nevetheless, a common idiom is to provide default values for parameters as in
function log(x,b)
b = b or 10
...
end
If you were told not to modify the parameters of functions, then there was probably a reasoning associated with that. Whatever that reasoning is would apply as much to Lua as to Java, since they have similar function argument semantics. Those reasons could be one or more of (but not limited to):
If you modify a parameter... you don't have it anymore. If you suddenly have a need for the original value you were passed, it's gone now.
Creating confusion, depending on how the parameters are named. The word "begin" suggests the beginning of something. If you change it, it isn't necessarily the beginning anymore, but merely the current element you're operating on.
Creating potential errors, if dealing with reference types (non-basic types in Java, tables and such in Lua). When you modify an object, you're changing it for everyone. Whereas incrementing an integer is just changing your local value. So if you're frequently modifying parameters, you still need to think about which ones you ought to be poking at and which ones you shouldn't be.
To put it another way, if you agreed with the suggestion for doing so in Java, then it applies just as much to Lua. If you didn't agree with the suggestion in Java, then you have no more reason to follow it under Lua.
In Lua functions, threads, tables and userdata types are passed by reference. So unless you have one of those you are working with a local copy anyway.
So in your example:
function do_stuff(begin, last)
--stuff
begin = begin + 1
last = last - 1
--stuff
end
begin and last are local non-reference variables in do_stuff's scope.
The only reason to make a copy of them is that you might want to store there initial value for later use. For that purpose you can either create a backup copy of the initial value or you create a working copy of it. Whatever you prefer.
Only make sure you know what is passed by reference and what by value so you avoid changing things you don't want to change and the other way around.

Inconsistent search behaviour in XE2?

Find in Files (all files in project, no regexp): "Integer("
("" are not part of the search term) with 'Whole words only' checked
returns these 6 lines:
PlanRoutines.BekijkRefplan( Integer( Items.Objects[ ItemIndex ] ));
PlanRoutines.BekijkRefplan( Integer( Items.Objects[ ItemIndex ] ));
Result := Integer( ComboBoxStatus.Properties.Items.Objects[ ComboBoxStatus.ItemIndex ] );
LNieuweStatus := Integer( ComboBoxNieuweStatus.Properties.Items.Objects[ ComboBoxNieuweStatus.ItemIndex ] );
LVanafStatus := Integer( ComboBoxVanafStatus.Properties.Items.Objects[ ComboBoxVanafStatus.ItemIndex ] );
LTotStatus := Integer( ComboBoxNieuweStatus.Properties.Items.Objects[ ComboBoxTotStatus.ItemIndex ] );
How come?
I would expect this first search to find no results (I accidentally had WWO checked when this happened)
With WWO unchecked, searching for " Integer(" (note the extra space in front) Delphi finds approx 100 lines including those 6 mentioned above.
Even the search dialog itself has weird behaviour:
If I copy or type "Integer(" into the search edit, then insert a space in front, the cursor jumps to the end of "Integer(".
This does not happen if I type another character in front of "Integer(", or if I type a space in front of e.g. "string", "string(" or "String("
Can anyone confirm? What is my Delphi XE2 Update 4 Hotfix 1 (16.0.4504.48759) doing (Win7 64bit)?
It's not a big deal, just curious.
(That initial search result is wrong, but I would not intentially do that search. WWO was still checked from a previous search).
Additional examples that surprise me: with WWO on, search "TMyEventHandlers." -> 0 results, search ":= true" -> 0 results, search "true)" -> plenty results, search "(Sender:" -> 0 results. I do not understand how Delphi behaves here. The only thing I can can conclude right now is 'Better make sure to only have chars/digits' in the search string when checking WWO.
then insert a space in front, the cursor jumps to the end of "Integer("
cannot confirm. Works like a charm.
win7 x64 / XE2 u4hf1 / Ide FixPack 5.0 / VersionCopntrol-Plus SVN
I would expect this first search to find no results
Basically that has little practical sense. Why search to find zero results ? to test IDE ?
To me i formulate WWO behaviour rather simple - both left and right from pattern there whould be distinct separate terms. And within brackets - there are those.

Delete line in TSTringlist

Hello i am writing some values to a stringlist. And would like to delete a value from the string list.
Currently I write to the string list like this.
FGamePlay.Locations.strings[0] := ('NumberOfLocations='+inttostr(NOL+1)); //add one to total
FGameplay.Locations.Add(inttostr(Position.x)+inttostr(Position.Y)+'=pos'); //add the location to list
This will return me a list like so
INDEX VALUE
[0] NumberOfLocations=4
[1] 23=pos
[2] 34=pos
[3] 24=pos
[4] 52=pos
Now i try to delete it like this
FGamePlay.Locations.Delete(FGamePlay.Locations.IndexOf(inttostr(ePosition.x)+inttostr(ePosition.Y)));
were ePosition.x + ePosition.Y will equal 23, 34,24,or 52. Thus it should delete the that line but instead when i add this delete line i get index out of bounds -1. I did stop the code just before this line and looked at Locations() and it had all these numbers in there. Also looked at epostion and the X,Y values were 34, thus correct too. Any idea?
thanks
Glen
When you uses the IndexOf function you must pass the exact string to find, in this case since you are adding the strings in this way
FGameplay.Locations.Add(inttostr(Position.x)+inttostr(Position.Y)+'=pos');
You must add the =pos to the string to search, something like
LIndex:=FGamePlay.Locations.IndexOf(inttostr(ePosition.x)+inttostr(ePosition.Y)+'=pos');
If LIndex>=0 then
FGamePlay.Locations.Delete(LIndex);
As RRUZ says, the string you are looking for to delete is missing the "=pos" suffix.
In order to debug this more effectively, you should break up the code a bit more. If you had this equivalent code:
str := inttostr(ePosition.x)+inttostr(ePosition.Y);
pos := FGamePlay.Locations.IndexOf(str);
FGamePlay.Locations.Delete(pos);
You would get an error on the pos := line, which would allow to to see the source of the error much more easily.
You could also consider making a function like:
function MakePosString(Position : Point);
begin
Result := inttostr(ePosition.x)+inttostr(ePosition.Y)+'=pos';
end;
Then you can call that function instead of reimplementing that code and you are guaranteed that your strings will be consistent.
Whilst I agree with everything everyone else has said about considering using a better data structure for the job at hand, I think for the sake of anyone with a similar problem in the future it is worth mentioning something that nobody else yet identified.
Your expression:
IntToStr(ePosition.x) + IntToStr(ePosition.y)
identifies the NAME of an entry in your string list, when considered as a name/value list. That is, a TStringList where each item is of the form "name=value". Whilst one way to fix your code is to append the rest of the string ('=pos') this of course only works when the "value" part of every named value is always "pos".
If there is the possibility that the "pos" value could be different or unknown for a given named value, then you can still find it by looking up the index of the item using just the name part:
itemName := IntToStr(ePosition.x) + IntToStr(ePosition.y);
itemIndex := fGamePlay.Locations.IndexOfName(itemName);
if itemIndex > -1 then
fGamePlay.Locations.Delete(IndexOfName(itemName));

matlab indexing into nameless matrix [duplicate]

For example, if I want to read the middle value from magic(5), I can do so like this:
M = magic(5);
value = M(3,3);
to get value == 13. I'd like to be able to do something like one of these:
value = magic(5)(3,3);
value = (magic(5))(3,3);
to dispense with the intermediate variable. However, MATLAB complains about Unbalanced or unexpected parenthesis or bracket on the first parenthesis before the 3.
Is it possible to read values from an array/matrix without first assigning it to a variable?
It actually is possible to do what you want, but you have to use the functional form of the indexing operator. When you perform an indexing operation using (), you are actually making a call to the subsref function. So, even though you can't do this:
value = magic(5)(3, 3);
You can do this:
value = subsref(magic(5), struct('type', '()', 'subs', {{3, 3}}));
Ugly, but possible. ;)
In general, you just have to change the indexing step to a function call so you don't have two sets of parentheses immediately following one another. Another way to do this would be to define your own anonymous function to do the subscripted indexing. For example:
subindex = #(A, r, c) A(r, c); % An anonymous function for 2-D indexing
value = subindex(magic(5), 3, 3); % Use the function to index the matrix
However, when all is said and done the temporary local variable solution is much more readable, and definitely what I would suggest.
There was just good blog post on Loren on the Art of Matlab a couple days ago with a couple gems that might help. In particular, using helper functions like:
paren = #(x, varargin) x(varargin{:});
curly = #(x, varargin) x{varargin{:}};
where paren() can be used like
paren(magic(5), 3, 3);
would return
ans = 16
I would also surmise that this will be faster than gnovice's answer, but I haven't checked (Use the profiler!!!). That being said, you also have to include these function definitions somewhere. I personally have made them independent functions in my path, because they are super useful.
These functions and others are now available in the Functional Programming Constructs add-on which is available through the MATLAB Add-On Explorer or on the File Exchange.
How do you feel about using undocumented features:
>> builtin('_paren', magic(5), 3, 3) %# M(3,3)
ans =
13
or for cell arrays:
>> builtin('_brace', num2cell(magic(5)), 3, 3) %# C{3,3}
ans =
13
Just like magic :)
UPDATE:
Bad news, the above hack doesn't work anymore in R2015b! That's fine, it was undocumented functionality and we cannot rely on it as a supported feature :)
For those wondering where to find this type of thing, look in the folder fullfile(matlabroot,'bin','registry'). There's a bunch of XML files there that list all kinds of goodies. Be warned that calling some of these functions directly can easily crash your MATLAB session.
At least in MATLAB 2013a you can use getfield like:
a=rand(5);
getfield(a,{1,2}) % etc
to get the element at (1,2)
unfortunately syntax like magic(5)(3,3) is not supported by matlab. you need to use temporary intermediate variables. you can free up the memory after use, e.g.
tmp = magic(3);
myVar = tmp(3,3);
clear tmp
Note that if you compare running times with the standard way (asign the result and then access entries), they are exactly the same.
subs=#(M,i,j) M(i,j);
>> for nit=1:10;tic;subs(magic(100),1:10,1:10);tlap(nit)=toc;end;mean(tlap)
ans =
0.0103
>> for nit=1:10,tic;M=magic(100); M(1:10,1:10);tlap(nit)=toc;end;mean(tlap)
ans =
0.0101
To my opinion, the bottom line is : MATLAB does not have pointers, you have to live with it.
It could be more simple if you make a new function:
function [ element ] = getElem( matrix, index1, index2 )
element = matrix(index1, index2);
end
and then use it:
value = getElem(magic(5), 3, 3);
Your initial notation is the most concise way to do this:
M = magic(5); %create
value = M(3,3); % extract useful data
clear M; %free memory
If you are doing this in a loop you can just reassign M every time and ignore the clear statement as well.
To complement Amro's answer, you can use feval instead of builtin. There is no difference, really, unless you try to overload the operator function:
BUILTIN(...) is the same as FEVAL(...) except that it will call the
original built-in version of the function even if an overloaded one
exists (for this to work, you must never overload
BUILTIN).
>> feval('_paren', magic(5), 3, 3) % M(3,3)
ans =
13
>> feval('_brace', num2cell(magic(5)), 3, 3) % C{3,3}
ans =
13
What's interesting is that feval seems to be just a tiny bit quicker than builtin (by ~3.5%), at least in Matlab 2013b, which is weird given that feval needs to check if the function is overloaded, unlike builtin:
>> tic; for i=1:1e6, feval('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 49.904117 seconds.
>> tic; for i=1:1e6, builtin('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 51.485339 seconds.

Resources