I need to find out how many time one word appears in the string, but the catch is that the word you need to find can have spaces in between, for example you want to see how many times word text appears in *tOeOxOt" and it would give you output 1, or for example in textt it would give you output 2, I have written this procedure in pascal for this
procedure search(x:integer; i:integer);
var
x2:integer;
begin
x2:=x+1;
while (x2<=n) and (x2>0) do begin
if myarray[x2]=mystring[i+1] then
if i=length(mystring)-1 then
final:=final+1
else
search(x2,i+1);
x2:=x2+1;
end;
end;
and it checks number of time it appears from one letter, for example if I have ttext it would only give me one because I only check from the first t so I call the function every time I find a t in the string, but this method is too slow for 2D arrays with many characters, like 1000x1000 so I am looking for a faster solution.
You could check the array twice, on the first run trough it remove all spaces.
On the second one use a compare function like this(x is the array in which you search, y is the sub-string you are searching for and i is the current element you are checking):
function compare(var x,y:myarray; i:integer):boolean;
var l:integer;
Begin
compare:=false;
for l:=1 to length(y) do Begin
if x[i+l] <> y[l] then Exit;
End;
compare:=true;
End;
on each element of your array.
Related
Given a field/string like 'G,H,1,AA,T,AAA,1,E,A,H,....'. The characters can be in any combination/order.
How do I search that string and return True when searching for just 'A' or 'AA'?
i.e. If doing a search for say 'A', it should only find the 'A' between the E & H.
Regards & TIA,
Ian
You can simply split your string into an array by your delimiter and search in that array, e.g.
function FindItem(const List, Item: string): Boolean;
var
SArr: TArray<string>;
S: string;
begin
Result := False;
//Separators could also be a parameter
SArr := List.Split([',']);
for S in SArr do
begin
//use S.Trim if needed
//use AnsiSameText(S, Item) for case insensitive check
if Item = S then
Exit(True);
end;
end;
If you need to search for multiple items in your data, you might want to sort the array and use a binary search.
TArray.Sort<string>(SArr);
Result := TArray.BinarySearch(SArr, Item, Tmp);
Another approach would be using regular expressions with word boundaries to search for whole words only
Result := TRegex.IsMatch(List, '\bA\b');
Split this string into a list, for example with TStringList.CommaText (alternatively, into an array with StrUtils.SplitString()).
Then, just walk through the list and check every string (or use TStrings.IndexOf() - note: it uses CaseSensitive property, as Remy mentioned in comments).
If you are going to make many queries for the same list - sort it and use an effective binary search (TStringList.Find()).
(Translated from German to Englisch)
I need help in this exercise :
Thread: String processing The user can make simple changes to an input sentence.
conditions
The program displays a menu for the user to select the following action. This is also displayed again after the action has been completed until the user terminates the program (a loop is therefore required).
The menu contains the following items, which should be executed when the specified letter is entered:
A. Enter the sentence
B. Determine the number of words
C. Determine the number of characters that are less than their sequence character
D. Replace all the words in the sentence with their uppercase initials
X. end
If the user enters a different letter, nothing happens or the menu is output again.
If the menu item A is selected, a prompt is issued to enter a set which is read into a string variable. This variable can not be changed by the actions of menu items B, C and D! Possibly. A copy of the set has to be prepared beforehand in another string variable.
In menu point B the number of all words in the block is to be counted. For simplicity, you can assume that there is always one space between two words. At the beginning and end of the sentence there are no spaces. The number of words is output after the calculation (e.g., "The set is 4 words").
If the user executes menu item C, the set is traversed character-by-character, and for each character it is checked whether it is smaller than its trailing character. Here is a simple character comparison (you can also write directly something like '1' <'d'). The number of characters so found is then output (e.g., "13 characters found in the sentence less than the trailing character").
In menu item D, the sentence is traversed and every word contained in it is replaced by its upper-case initial character. The capitalization is of course only made if the first character is a letter, otherwise the character remains unchanged. You can assume that the sentence never starts or ends with a space. Between two words there is always exactly one space and so it should be between the initial letters. For example, from "123 good mood" becomes "1 G L".
It is not permissible here to build up a completely new string piece by piece! Instead, you should work in a loop on a copy of the original sentence with pos, copy, length, delete and insert! It is also forbidden to "gather" the initial characters all at the beginning or end of the string; These should be inserted directly into the string at the position of the corresponding word!
Furthermore, a string can not be accessed at menu point D, because the work with string routines is to be practised explicitly here. Menu items B, C and D may only be selectable if a record has already been entered. Otherwise nothing happens or a fault message is entered when entering B, C or D in the menu and the menu is output again.
Each call to the menu items B, C or D will always work on the original set entered by the user and not on a set that has already been altered by previously executed menu items!
By entering the menu item A again, the entered block can be overwritten by a new one.
With an 'X' the user can terminate the program.
Use wherever it is the predefined string functions and do not write it yourself with difficulty loops, etc.! However, the use of the strreplace or reverseString functions is forbidden!
Here's my work till now, I only have problems with part D:
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
const
lz = ' ';
var
Satz: string;
Buchstabe: char;
i, p, j, zaehler2, index, count: integer;
writeln('A) Write Sentence');
readln(Satz);
'D':
begin
index := 2;
insert(lz, Satz, length(Satz)+1);
count := (pos(lz,Satz));
repeat
delete(Satz, index,(count - index));
index := index + 2;
count := pos(lz,copy(Satz,index,(length(Satz)-index)))+index-1;
until ;
writeln(uppercase(Satz));
end
I'm glad you've found your own solution, well done!
While you've been doing that, I've been writing the answer below and, as I
have finished it, I thought I'd post it here to show you another way
to go about the problem of extracting words from a string. There are
dozens of ways of doing that task, but I hope the one I've used is fairly
easy to follow.
Maybe the reason you were having a problem with this is that your string-indexing
expressions are a bit too complicated. I bet if you come back to your code in 6 months
it will take you a while to figure out what it is supposed to be doing and longer
to tell whether it is actually doing it. The key to avoiding problems like that
is to break your code up into chucks which are easier to follow and easier
to test. So instead of just telling you what your repeat condition should be,
I'll show you a way which is easier to follow.
The first thing to do is to extract a singe word from the input. So, first thing
I've written is a function, ExtractFirstWord which returns the first word in
the input string, whether or not the input includes spaces, and also returns
a Remainder string which is what is left or the input string after the first
word (and any spaces immediately following it have been removed). This is done
using some simple while loops which are coded to skip over the leading spaces
and then build a string from the non-space characters which follow.
Code
const
lz = ' ';
var
Satz: string;
FirstWord : String;
Remainder : String;
function ExtractFirstWord(const InputStr : String; var Remainder : String) : String;
var
P : Integer;
WordStart : Integer;
begin
Result := '';
P := 1;
// The following skips over any spaces at the start of InputStr
while (P <= Length(InputStr)) and (InputStr[P] = lz) do
Inc(P);
// Now we know where the first word starts
WordStart := P;
// Now we can get the first word, if there is one
while (P <= Length(InputStr)) and (InputStr[P] <> lz) do
Inc(P);
Result := Copy(InputStr, WordStart, P - WordStart);
Remainder := Copy(InputStr, P, Length(InputStr));
// the following is one way to remove spaces at the start of Remainder
while (Length(Remainder) > 0) and (Remainder[1] = lz) do
Delete(Remainder, 1, Length(lz));
// instead you could do something simlar to the first `while` loop above
end;
begin
Satz := ' cat dog ';
repeat
FirstWord := ExtractFirstWord(Satz, Remainder);
FirstWord := UpperCase(FirstWord);
Satz := Remainder;
writeln('First word: ', FirstWord, ' remainder: ', Remainder);
until Remainder = '';
readln;
end.
This particular way of doing it is not an ideal fit with the other requirements
specified in your task but should be easily adaptable to them. E.g, the upper-casing of words could be done "in place" on the input string by upper-casing the current character of it in the second While loop.
Btw, if you are using Delphi or Free Pascal/Lazarus, there is a much simpler
way of extracting the words from a string. It uses a TStringList. Try
looking it up in the online help and have a thing about how you might use it
to do the task.
I've been searching for days on how to do this, and nothing is exactly what I need to do (or I just don't understand how to implement the solution).
What I need to do is parse a string, which is a street address, into a TStringList, and then set those strings from the list to variables I can then pass to the rest of the program. I already have the list working ok:
var
AddressList : TStringList;
i : integer;
begin
AddressList := TStringList.Create;
AddressList.Delimiter := ' ';
AddressList.DelimitedText := RawAddressStr; //RawAddressStr is parsed from another file and the string result is something like '1234 Dark Souls Lane Wyoming, MI 48419'
for i := 0 to AddressList.Count-1 do
AddressList.Strings[i]; //Not sure what to do here
The issue is that the address isn't always the same length. The address will sometimes be like '1234 Dark Souls Two Lane Wyoming...' or '1234 Dark Lane Wyoming...'
So I need to be able to use this TStringList to set the Zip, State and City into variables for later use. I would use TStringList.Find, but the ZIP code isn't always the same. Is there a way to get the last string and then go backwards from there? (Going backwards because once I get the City, State ZIP I can remove that from the RawAddressStr and then set the rest to the address string.)
Thanks.
Edit, here's the code I needed (thanks to below comments):
AddressList := TStringList.Create;
AddressList.Delimiter := ' ';
AddressList.DelimitedText := RawAddressStr;
for i := 0 to AddressList.Count-1 do
LastIndex := AddressList.Count - 1;
ZipStr := AddressList[LastIndex];
StateStr := AddressList[LastIndex - 1];
CityStr := AddressList[LastIndex - 2];
Now I can use these with StringReplace to take out the City, State Zip from the full address string, and set that as the Address string to use.
I am a little bit unsure of exactly what you're looking for since you ask
of how to get the last string of your StringList,
then at the end of the question you ask
Is there a way to get the last string and then go backwards from
there?
If you want to get the last string of a StringList you can use
var AddressList : TStringList;
MyString: string;
begin
MyString := AddressList.Last; //this...
MyString := AddressList.Strings[AddressList.Count-1]; //...and this is essentially the same thing
end;
if you would like to for-loop in reverse or backwards you should write:
for i := AddressList.Count-1 downto 0 do
AddressList.Strings[i]; //Not sure what to do here
Notice that it says "DOWNTO" and not "TO".
Now, if you would like to stop at a specific string, lets say the ZIP code,
you need to make your software understand what it is reading.
Which one of the delimited strings is the City?
Which one is the address?
To the software, a string is a string, it doesn't know, or even care what it is reading.
So I would like to suggest that you have a database of citys which it can compare the strings
of AddressList t,o and see if there is a match.
You could also implement some logic in to your algorithm.
If you know that the last string of your delimited AdressList string always is the City-name,
then you know you have the city name right there which you can use.
If you know that everything between the ZIP code and the City Name is the Street Address,
then just copy everything between the ZIP and the City-name and use that as a Street-name information.
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.
I have a List Box or a List View with items. And I have a String List with the same items (strings) as the List Box/List View. I want to delete all selected items in the List Box/List View from the String List.
How to do?
for i:=0 to ListBox.Count-1 do
if ListBox.Selected[i] then
StringList1.Delete(i); // I cannot know exactly an index, other strings move up
for i := ListBox.Count - 1 downto 0 do
if ListBox.Selected[i] then
StringList1.Delete(i);
The trick is to run the loop in reverse order:
for i := ListBox.Count-1 downto 0 do
if ListBox.Selected[i] then
StringList1.Delete(i);
This way, the act of deleting an item only changes the indices of elements later in the list, and those elements have already been processed.
Solution provided by Andreas and David assumes that the strings are exactly in same order in both ListBox and StringList. This is good assumption as you don't indicate otherwise, but in case it is not true you can use StringList's IndexOf method to find the index of the string (if the StringList is sorted, use Find instead). Something like
var x, Idx: Integer;
for x := ListBox.Count - 1 downto 0 do begin
if ListBox.Selected[x] then begin
idx := StringList.IndexOf(ListBox.Items[x]);
if(idx <> -1)then StringList.Delete(idx);
end;
end;
How about doing it the other way round (adding instead of deleting)?
StringList1.Clear;
for i:=0 to ListBox.Count-1 do
if not ListBox.Selected[i] then StringList1.Add(ListBox.Items(i));