I'd like to know how I can delete numbers from a String. I try to use StringReplace and I don't know how to tell the function that I want to replace numbers.
Here's what I tried:
StringReplace(mString, [0..9], '', [rfReplaceAll, rfIgnoreCase]);
Simple but effective. Can be optimized, but should get you what you need as a start:
function RemoveNumbers(const aString: string): string;
var
C: Char;
begin
Result := '';
for C in aString do begin
if not CharInSet(C, ['0'..'9']) then
begin
Result := Result + C;
end;
end;
end;
Pretty quick inplace version.
procedure RemoveDigits(var s: string);
var
i, j: Integer;
pc: PChar;
begin
j := 0;
pc := PChar(#s[1]);
for i := 0 to Length(s) - 1 do
if pc[i] in ['0'..'9'] then
//if CharInSet(pc[i], ['0'..'9']) for Unicode version
Inc(j)
else
pc[i - j] := pc[i];
SetLength(s, Length(s) - j);
end;
This has the same output as Nick's version, but this is more than 3 times as fast with short strings. The longer the text, the bigger the difference.
function RemoveNumbers2(const aString: string): string;
var
C:Char; Index:Integer;
begin
Result := '';
SetLength(Result, Length(aString));
Index := 1;
for C in aString do
if not CharInSet(C, ['0' .. '9']) then
begin
Result[Index] := C;
Inc(Index);
end;
SetLength(Result, Index-1);
end;
Don't waste precious CPU cycles if you don't have to.
Well I was tired of looking for already build functions so I've create my own:
function RemoveNumbers(const AValue: string): string;
var
iCar : Integer;
mBuffer : string;
begin
mBuffer := AValue;
for iCar := Length(mBuffer) downto 1 do
begin
if (mBuffer[iCar] in ['0'..'9']) then
Delete(mBuffer,iCar,1);
end;
Result := mBuffer;
end;
use this
function RemoveNonAlpha(srcStr : string) : string;
const
CHARS = ['0'..'9'];
var i : integer;
begin
result:='';
for i:=0 to length(srcStr) do
if (srcstr[i] in CHARS) then
result:=result+srcStr[i];
end ;
you can call it like this
edit2.text:=RemoveNonAlpha(edit1.text);
StringReplace does not accept a set as the second argument. Maybe someone will have a more suitable approach, but this works:
StringReplace(mString, '0', '', [rfReplaceAll, rfIgnoreCase]);
StringReplace(mString, '1', '', [rfReplaceAll, rfIgnoreCase]);
StringReplace(mString, '2', '', [rfReplaceAll, rfIgnoreCase]);
etc.
Related
I have a string, I set the coordinates for the selection of letters, but sometimes they mix up and I asked the coordinates with the numbers, how do I make that if for example '105DYUDXB28DYU13' had it done 'DYUDXBDYU'
tmpT.Put:=Trim(MidStr(S,8,6))+Trim(MidStr(S,37,3));
A solution with good performance is as follows (uses Character):
function RemoveNumbers(const AString: string): string;
var
i, j: integer;
begin
SetLength(result, AString.Length);
j := 0;
for i := 1 to AString.Length do
if not AString[i].IsDigit then
begin
inc(j);
result[j] := AString[i];
end;
SetLength(result, j);
end;
This function uses a few language and library features introduced after Delphi 7. To make this work in Delphi 7, you need to rewrite it slightly:
function RemoveNumbers(const AString: string): string;
var
i, j: integer;
begin
SetLength(result, Length(AString));
j := 0;
for i := 1 to Length(AString) do
if not (AString[i] in ['0'..'9']) then
begin
inc(j);
result[j] := AString[i];
end;
SetLength(result, j);
end;
The fine print: TCharHelper.IsDigit is Unicode-aware, and so it will return true for all Unicode digits. For instance, it will return true for
٣ (U+0663: ARABIC-INDIC DIGIT THREE),
੪ (U+0A6A: GURMUKHI DIGIT FOUR),
௫ (U+0BEB: TAMIL DIGIT FIVE),
៥ (U+17E5: KHMER DIGIT FIVE), and
᠗ (U+1817: MONGOLIAN DIGIT SEVEN).
If you only want to treat the characters '0'..'9' as digits, you can use the modernized version of the Delphi 7 test:
if not CharInSet(AString[i], ['0'..'9']) then
Also code with procedure and var parameter:
procedure RemoveNumbers(var Text: String);
var
Index: Integer;
begin
for Index := Length(Text) downto 1 do
if Text[Index] in ['0'..'9']
then Text := Copy(Text, 1, Index - 1) + Copy(Text, Index + 1, Length(Text) - Index);
end;
var
Text: String;
begin
Text := '105DYUDXB28DYU13';
RemoveNumbers(Text);
WriteLn(Text);
end.
How can I split a string in Inno Setup?
Is there any special function in Inno Setup to split the string?
I want to get the following from the string '11.2.0.16':
tokens: array of string = ('11', '0', '2', '16');
Thanks in advance!
For anyone who prefers the function format, I have modified #cezarlamann's answer:
function StrSplit(Text: String; Separator: String): TArrayOfString;
var
i, p: Integer;
Dest: TArrayOfString;
begin
i := 0;
repeat
SetArrayLength(Dest, i+1);
p := Pos(Separator,Text);
if p > 0 then begin
Dest[i] := Copy(Text, 1, p-1);
Text := Copy(Text, p + Length(Separator), Length(Text));
i := i + 1;
end else begin
Dest[i] := Text;
Text := '';
end;
until Length(Text)=0;
Result := Dest
end;
I've been looking for the same thing today...
This one works just fine on Inno Setup Scripts. Paste this excerpt inside your script before the procedure/function which will call this "split" procedure.
You can also modify this onto a function, if you desire...
procedure Explode(var Dest: TArrayOfString; Text: String; Separator: String);
var
i, p: Integer;
begin
i := 0;
repeat
SetArrayLength(Dest, i+1);
p := Pos(Separator,Text);
if p > 0 then begin
Dest[i] := Copy(Text, 1, p-1);
Text := Copy(Text, p + Length(Separator), Length(Text));
i := i + 1;
end else begin
Dest[i] := Text;
Text := '';
end;
until Length(Text)=0;
end;
procedure Whatever();
var
str: String;
strArray: TArrayOfString;
i: Integer;
begin
Explode(strArray,str,'.');
for i:=0 to GetArrayLength(strArray)-1 do begin
{ do something }
end;
end;
Taken from here
Here's what I use:
procedure SplitString(S, Delim: string; var Dest: TArrayOfString);
var
Temp: string;
I, P: Integer;
begin
Temp := S;
I := StringChangeEx(Temp, Delim, '', true);
SetArrayLength(Dest, I + 1);
for I := 0 to GetArrayLength(Dest) - 1 do
begin
P := Pos(Delim, S);
if P > 0 then
begin
Dest[I] := Copy(S, 1, P - 1);
Delete(S, 1, P + Length(Delim) - 1);
end
else
Dest[I] := S;
end;
end;
This version avoids repeated array resizing by counting the delimiters using StringChangeEx and setting the array size only once. Since we then know the array size, we can just use a for loop. I also opted for Delete rather than Copy, which (IMO) makes the code easier to read. (This version also fixes the bug where the split does not occur correctly if the delimiter is longer than 1 character.)
If there is a possibility that the delimiter could also be right at the end of the string, then this is what I used (modified from #timyha's answer)
function StrSplit(Text: String; Separator: String): TArrayOfString;
var
i, p: Integer;
Dest: TArrayOfString;
begin
i := 0;
repeat
SetArrayLength(Dest, i+1);
p := Pos(Separator,Text);
if p > 0 then begin
Dest[i] := Copy(Text, 1, p-1);
Text := Copy(Text, p + Length(Separator), Length(Text));
i := i + 1;
//add an empty string if delim was at the end
if Text = '' then begin
Dest[i]:='';
i := i + 1;
end;
end else begin
Dest[i] := Text;
Text := '';
end;
until Length(Text)=0;
Result := Dest
end;
I have a random string of numbers
(numbers can only be used once, only from 1-9, almost any length(min 1,max 9)):
var
Input: String;
begin
Input := '431829576'; //User inputs random numbers
And now I need to get specified number to front. How about 5.
var
Number: Integer;
begin
Number := 5;
and function executes with result 543182976.
I don't have any ideas how to make a function like this, Thanks.
Do you mean like this?
function ForceDigitInFront(const S: string; const Digit: Char): string;
begin
result := Digit + StringReplace(S, Digit, '', []);
end;
A more lightweight solution is
function ForceDigitInFront(const S: string; const Digit: Char): string;
var
i: Integer;
begin
result := S;
for i := 1 to Length(S) do
if result[i] = Digit then
begin
Delete(result, i, 1);
break;
end;
result := Digit + result;
end;
You could do it this way :
function ForceDigitInFront(const S: string; const Digit: Char): string;
var
dPos : Integer;
begin
Result := s;
dPos := Pos( Digit,S);
if (dPos <> 0) then begin // Only apply Digit in front if Digit exists !?
Delete( Result,dPos,1);
Result := Digit + Result;
end;
end;
If Digit is not in input string, the digit is not added here, but change this if it does not fit your implementation.
Here is a solution that reduces the numer of String allocations needed, as well as checks if the digit is already in the front:
function ForceDigitInFront(const S: string; const Digit: Char): string;
var
dPos : Integer;
begin
Result := s;
for dPos := 1 to Length(Result) do
begin
if Result[dPos] = Digit then
begin
if dPos > 1 then
begin
UniqueString(Result);
Move(Result[1], Result[2], (dPos-1) * SizeOf(Char));
Result[1] := Digit;
end;
Exit;
end;
end;
end;
I have a string of delimited text ie:
Value1:Value2:Value3:Value4:Value5:Value6
How would I extract, for example, a specific value Ie:
Label.caption := GetValuefromDelimitedText(2); to get Value2
Thanks in advance
Paul
Something like that - if you like compact code (but not as performant as Davids):
function GetValueFromDelimitedText(const s: string; Separator: char; Index: Integer): string;
var sl : TStringList;
begin
Result := '';
sl := TStringList.Create;
try
sl.Delimiter := Separator;
sl.DelimitedText := s;
if sl.Count > index then
Result := sl[index];
finally
sl.Free;
end;
end;
Hope that helps
This should do it:
function GetValueFromDelimitedText(
const s: string;
const Separator: char;
const Index: Integer
): string;
var
i, ItemIndex, Start: Integer;
begin
ItemIndex := 1;
Start := 1;
for i := 1 to Length(s) do begin
if s[i]=Separator then begin
if ItemIndex=Index then begin
Result := Copy(s, Start, i-Start);
exit;
end;
inc(ItemIndex);
Start := i+1;
end;
end;
if ItemIndex=Index then begin
Result := Copy(s, Start, Length(s)-Start+1);
end else begin
Result := '';
end;
end;
This version allows you to specify the separator, you would obviously pass ':'. If you ask for an item beyond the end then the function will return the empty string. You could change that to an exception if you preferred. Finally, I have arranged that this uses 1-based indexing as per your example, but I personally would choose 0-based indexing.
If using Delphi XE or higher you can also use StrUtils.SplitString like this:
function GetValueFromDelimitedText (const Str: string; Separator: Char; Index: Integer) : string;
begin
Result := SplitString (Str, Separator) [Index];
end;
In production code, you should check that Index is indeed a valid index.
This method returns a TStringDynArray (a dynamic array of strings) so you can also use it like this (using enumerators):
for Str in SplitString (Str, Separator) do
Writeln (Str);
which can be very useful IMHO.
I have a string comprising numerous words. How do I find and count the total amount of times that a particular word appears?
E.g "hello-apple-banana-hello-pear"
How would I go about finding all the "hello's" in the example above?
Thanks.
In Delphi XE you can use StrUtils.SplitString.
Something like this
var
Words: TstringDynArray;
Word: string;
WordCount: Integer;
begin
WordCount := 0;
Words := SplitString('hello-apple-banana-hello-pear', '-');
for Word in Words do
begin
if Word = 'hello' then
inc(WordCount);
end;
This would depend entirely on how you define a word and the text from which you wish to pull the words. If a "word" is everything between spaces, or "-" in your example, then it becomes a fairly simple task. If, however, you want to deal with hyphenated words, abbreviations, contractions, etc. then it becomes a lot more difficult.
More information please.
EDIT: After rereading your post, and if the example you give is the only one you want, then I'd suggest this:
function CountStr(const ASearchFor, ASearchIn : string) : Integer;
var
Start : Integer;
begin
Result := 0;
Start := Pos(ASearchFor, ASearchIn);
while Start > 0 do
begin
Inc(Result);
Start := PosEx(ASearchFor, ASearchIn, Start + 1);
end;
end;
This will catch ALL instances of a sequence of characters.
I'm sure there is plenty of code around to do this sort of thing, but it's easy enough to do it yourself with the help of Generics.Collections.TDictionary<K,V>.
program WordCount;
{$APPTYPE CONSOLE}
uses
SysUtils, Character, Generics.Collections;
function IsSeparator(const c: char): Boolean;
begin
Result := TCharacter.IsWhiteSpace(c);//replace this with whatever you want
end;
procedure PopulateWordDictionary(const s: string; dict: TDictionary<string, Integer>);
procedure AddItem(Item: string);
var
Count: Integer;
begin
if Item='' then
exit;
Item := LowerCase(Item);
if dict.TryGetValue(Item, Count) then
dict[Item] := Count+1
else
dict.Add(Item, 1);
end;
var
i, len, Start: Integer;
Item: string;
begin
len := Length(s);
Start := 1;
for i := 1 to len do begin
if IsSeparator(s[i]) then begin
AddItem(Copy(s, Start, i-Start));
Start := i+1;
end;
end;
AddItem(Copy(s, Start, len-Start+1));
end;
procedure Main;
var
dict: TDictionary<string, Integer>;
pair: TPair<string, Integer>;
begin
dict := TDictionary<string, Integer>.Create;
try
PopulateWordDictionary('hello apple banana Hello pear', dict);
for pair in dict do
Writeln(pair.Key, ': ', pair.Value);
finally
dict.Free;
end;
end;
begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Output:
hello: 2
banana: 1
apple: 1
pear: 1
Note: I'm working with Delphi 2010 and don't have SplitString() available.
A very clever implementation I saw somewhere on the web:
{ Returns a count of the number of occurences of SubText in Text }
function CountOccurences( const SubText: string;
const Text: string): Integer;
begin
if (SubText = '') OR (Text = '') OR (Pos(SubText, Text) = 0) then
Result := 0
else
Result := (Length(Text) - Length(StringReplace(Text, SubText, '', [rfReplaceAll]))) div Length(subtext);
end; { CountOccurences }