Swapping characters using TRegEx in Delphi - regex

I need to swap character dot with comma and vice versa simultaneously.
function TformMain.SwapString(input, fromSymbol, toSymbol: String): String;
begin
Result := AnsiReplaceStr(input, fromSymbol, '_'); //100,200_00
Result := AnsiReplaceStr(Result, toSymbol, fromSymbol); //100.200_00
Result := AnsiReplaceStr(Result, '_', toSymbol); //100.200,00
end;
How to do this using TRegEx in Delphi Rio?

Although this is not an answer to your question (how to do this using regular expressions), I'd like to point out that this task can be performed with much greater runtime performance using a simple loop:
function SwapPeriodComma(const S: string): string;
var
i: Integer;
begin
Result := S;
for i := 1 to S.Length do
case S[i] of
'.':
Result[i] := ',';
',':
Result[i] := '.';
end;
end;
This is much faster than both the AnsiReplaceStr approach and the regular expression approach.
Generalised to any two characters:
function SwapChars(const S: string; C1, C2: Char): string;
var
i: Integer;
begin
Result := S;
for i := 1 to S.Length do
if S[i] = C1 then
Result[i] := C2
else if S[i] = C2 then
Result[i] := C1;
end;
(If you are OK with a procedure instead of a function, you can do this in-place and save memory and gain speed. But most likely you don't need such optimisations.)

Related

Calculation in one field

i'm new to sql and pl/sql. To practice I was giving an assignment to make a calculator. That part works. But they also want the possibility to type the calculation in the text field and then it needs to work. For example 4+4 (then the = button or enter on your keyboard) or 4+6-3=.
My calculator with buttons works, but not if I type a calculation in the text field. Can anyone help me with this?
This is the code I have in my total:
declare
l_operator varchar2(1) := :P3_OPERATOR;
l_value1 number := :P3_VALUE1;
l_value2 number := :P3_VALUE2;
l_result number := nvl(:P3_VALUE1,0);
begin
case l_operator
when '+' then
l_result := l_value1 + l_value2;
when '-' then
l_result := l_value1 - l_value2;
when '*' then
l_result := l_value1 * l_value2;
when '/' then
l_result := l_value1 / l_value2;
else
null;
end case;
:P3_OPERATOR := null;
:P3_VALUE2 := null;
:P3_VALUE1 := l_result;
:P3_NUMBERFIELD := l_result;
end;
with this for als extra for the +, -, * and \ .
:P12_OPERATOR := '*';
:P12_NUMBERFIELD := :P12_OPERATOR;
and this is the code for all my number buttons:
begin
if :P12_OPERATOR is null then
:P12_VALUE1 := :P12_VALUE1 || 4;
:P12_NUMBERFIELD := :P12_VALUE1;
elsif :P12_OPERATOR is not null then
:P12_VALUE2 := :P12_VALUE2 || 4;
:P12_NUMBERFIELD := :P12_VALUE2;
end if;
end;
This is not a typical way to use SQL or PL/SQL (or APEX which it looks like you are also using)!
You could evaluate any expression typed in with code like this:
begin
execute immediate 'select ' || :P3_NUMBERFIELD || ' from dual' into l_result;
exception
when others then
l_result := 'Invalid input';
end;
The exception part is to stop the calculator going wrong if the user types in nonsense like "hello world" instead of an arithmetic expression. The user would need to type in an expression like 4+4 without typing the equals sign, and then press a button to invoke the process to calculate the result.

Delphi - How can I extract the digits from a character string?

I was developing a program that validate a CPF, a type of document of my country. I already did all the math. But in the input Edit1, the user will insert like:
123.456.789-00
I have to get only the numbers, without the hyphen and the dots, to my calcs worth.
I'm newbie with Delphi, but I think that's simple. How can I do that? Thanks for all
You can use
text := '123.456.789-00'
text := TRegEx.Replace(text, '\D', '')
Here, \D matches any non-digit symbol that is replaced with an empty string.
Result is 12345678900 (see regex demo).
Using David's suggestion, iterate your input string and remove characters that aren't numbers.
{$APPTYPE CONSOLE}
function GetNumbers(const Value: string): string;
var
ch: char;
Index, Count: integer;
begin
SetLength(Result, Length(Value));
Count := 0;
for Index := 1 to length(Value) do
begin
ch := Value[Index];
if (ch >= '0') and (ch <='9') then
begin
inc(Count);
Result[Count] := ch;
end;
end;
SetLength(Result, Count);
end;
begin
Writeln(GetNumbers('123.456.789-00'));
Readln;
end.

Regular Expressions and TMatch.Groups.Count

I'm porting some classes from the Apache Commons library, and I found the following behaviour strange. I have a regular expression defined as
const
IPV4_REGEX = '^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$';
and I use it as follows:
ipv4Validator: TRegEx;
ipv4Validator := TRegEx.Create(IPV4_REGEX);
When I use it to match an IP address, the following code returns false - the debugger shows that Match.Groups.Count is 5, which I didn't expect.
var
Match: TMatch;
begin
Match := ipv4Validator.Match(inet4Address);
if Match.Groups.Count <> 4 then
Exit(false);
Is this the correct behaviour of TMatch.Groups.Count?
Just in case, here's the full code of my class. Notice that I have commented the offending line, because it made my tests fail.
unit InetAddressValidator;
interface
uses RegularExpressions;
const
IPV4_REGEX = '^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$';
type
TInetAddressValidator = class
private
ipv4Validator: TRegEx;
public
constructor Create; overload;
function isValid(const inetAddress: String): Boolean;
function isValidInet4Address(const inet4Address: String): Boolean;
end;
implementation
uses SysUtils;
constructor TInetAddressValidator.Create;
begin
inherited;
ipv4Validator := TRegEx.Create(IPV4_REGEX);
end;
function TInetAddressValidator.isValid(const inetAddress: String): Boolean;
begin
Result := isValidInet4Address(inetAddress);
end;
function TInetAddressValidator.isValidInet4Address(const inet4Address
: String): Boolean;
var
Match: TMatch;
IpSegment: Integer;
i: Integer;
begin
Match := ipv4Validator.Match(inet4Address);
// if Match.Groups.Count <> 4 then
// Exit(false);
IpSegment := 0;
for i := 1 to Match.Groups.Count - 1 do
begin
try
IpSegment := StrToInt(Match.Groups[i].Value);
except
Exit(false);
end;
if IpSegment > 255 then
Exit(false);
end;
Result := true;
end;
end.
Match.Groups[0] contains the whole expression, so this is correct.
TGroupcollection constructor:
constructor TGroupCollection.Create(ARegEx: TPerlRegEx;
const AValue: UTF8String; AIndex, ALength: Integer; ASuccess: Boolean);
var
I: Integer;
begin
FRegEx := ARegEx;
/// populate collection;
if ASuccess then
begin
SetLength(FList, FRegEx.GroupCount + 1);
for I := 0 to Length(FList) - 1 do
FList[I] := TGroup.Create(AValue, FRegEx.GroupOffsets[I], FRegEx.GroupLengths[I], ASuccess);
end;
end;
As you can see the internal Flist (TArray<TGroup>) is initiated with the number of groups + 1. FList[0] receives a group with offset 1 and the whole expression length. This behaviour is not documented.
Delphi's TRegEx is designed to mimic .NET's Regex class, which also adds the overall regex match to Match.Groups.Count. .NET does this so that the GroupCollection class can implement the ICollection interface.
In Java Matcher.group(0) also returns the overall regex match. Matcher.groupCount() returns the number of groups excluding the overall match. Most regex libraries do it this way.

Delphi extract string between to 2 tags

How would I go about extracting text between 2 html tags using delphi?
Here is an example string.
blah blah blah<tag>text I want to keep</tag>blah blah blah
and I want to extract this part of it.
<tag>text I want to keep</tag>
(basically removing all the blah blah blah garbage that comes before and after the <tag> & </tag> strings which I also want to keep.
Like I said, I am sure this is extremely easy for those who know, but I just cannot wrap my head around it at the moment. Thanks in advance for your replies.
If you have Delphi XE, you can use the new RegularExpressions unit:
ResultString := TRegEx.Match(SubjectString, '(?si)<tag>.*?</tag>').Value;
If you have an older version of Delphi, you can use a 3rd party regex component such as TPerlRegEx:
Regex := TPerlRegEx.Create(nil);
Regex.RegEx := '(?si)<tag>.*?</tag>';
Regex.Subject := SubjectString;
if Regex.Match then ResultString := Regex.MatchedExpression;
This depends entirely on how your input looks.
Update First I wrote a few solutions for special cases, but after the OP explained a bit more about the details, I had to generalize them a bit. Here is the most general code:
function ExtractTextInsideGivenTagEx(const Tag, Text: string): string;
var
StartPos1, StartPos2, EndPos: integer;
i: Integer;
begin
result := '';
StartPos1 := Pos('<' + Tag, Text);
EndPos := Pos('</' + Tag + '>', Text);
StartPos2 := 0;
for i := StartPos1 + length(Tag) + 1 to EndPos do
if Text[i] = '>' then
begin
StartPos2 := i + 1;
break;
end;
if (StartPos2 > 0) and (EndPos > StartPos2) then
result := Copy(Text, StartPos2, EndPos - StartPos2);
end;
function ExtractTagAndTextInsideGivenTagEx(const Tag, Text: string): string;
var
StartPos, EndPos: integer;
begin
result := '';
StartPos := Pos('<' + Tag, Text);
EndPos := Pos('</' + Tag + '>', Text);
if (StartPos > 0) and (EndPos > StartPos) then
result := Copy(Text, StartPos, EndPos - StartPos + length(Tag) + 3);
end;
Sample usage
ExtractTextInsideGivenTagEx('tag',
'blah <i>blah</i> <b>blah<tag a="2" b="4">text I want to keep</tag>blah blah </b>blah')
returns
text I want to keep
whereas
ExtractTagAndTextInsideGivenTagEx('tag',
'blah <i>blah</i> <b>blah<tag a="2" b="4">text I want to keep</tag>blah blah </b>blah')
returns
<tag a="2" b="4">text I want to keep</tag>
you can build an function using the pos the copy functions.
see this sample.
Function ExtractBetweenTags(Const Value,TagI,TagF:string):string;
var
i,f : integer;
begin
i:=Pos(TagI,Value);
f:=Pos(TagF,Value);
if (i>0) and (f>i) then
Result:=Copy(Value,i+length(TagI),f-i-length(TagF)+1);
end;
Function ExtractWithTags(Const Value,TagI,TagF:string):string;
var
i,f : integer;
begin
i:=Pos(TagI,Value);
f:=Pos(TagF,Value);
if (i>0) and (f>i) then
Result:=Copy(Value,i,f-i+length(TagF));
end;
and call like this
StrValue:='blah blah blah<tag> text I want to keep</tag>blah blah blah';
NewValue:=ExtractBetweenTags(StrValue,'<tag>','</tag>');//returns 'text I want to keep'
NewValue:=ExtractWithTags(StrValue,'<tag>','</tag>');//returns '<tag>text I want to keep</tag>'
I find that this version is more versatile because it isnt limited to one occurence of the tags. It searches for the next endtag after the starttag.
Function ExtractBetweenTags(Const Line, TagI, TagF: string): string;
var
i, f : integer;
begin
i := Pos(TagI, Line);
f := Pos(TagF, Copy(Line, i+length(TagI), MAXINT));
if (i > 0) and (f > 0) then
Result:= Copy(Line, i+length(TagI), f-1);
end;

PL/SQL key-value String using Regex

I have a String stored in a table in the following key-value format: "Key1☺Value1☺Key2☺Value2☺KeyN☺ValueN☺".
Given a Key how can I extract the Value? Is regex the easiest way to handle this? I am new to PL/SQL as well as Regex.
In this case, I would use just a regular split and iterate through the resulting array.
public string GetValue(string keyValuePairedInput, string key, char separator)
{
var split = keyValuePairedInput.Split(separator);
if(split.Lenght % 2 == 1)
throw new KeyWithoutValueException();
for(int i = 0; i < split.Lenght; i += 2)
{
if(split[i] == key)
return split[i + 1];
}
throw new KeyNotFoundException();
}
(this was not compiled and is not pl/sql anyway, treat it as pseudocode ☺)
OK I hear your comment...
Making use of pl/sql functions, you might be able to use something like this:
select 'key' as keyValue,
(instr(keyValueStringField, keyValue) + length(keyValue) + 1) as valueIndex,
substr(keyValueStringField, valueIndex, instr(keyValueStringField, '\1', valueIndex) - valueIndex) as value
from Table
For this kind of string slicing and dicing in PL/SQL you will probably have to use regular expressions. Oracle has a number of regular expression functions you can use. The most commonly used one is REGEXP_LIKE which is very similar to the LIKE operator but does RegEx matching.
However you probably need to use REGEXP_INSTR to find the positions where the separators are then use the SUBSTR function to slice up the string at the matched positions. You could also consider using REGEXP_SUBSTR which does the RegEx matching and slicing in one step.
As an alternative to regular expressions...
Assuming you have an input such as this:
Key1,Value1|Key2,Value2|Key3,Value3
You could use some PL/SQL as shown below:
FUNCTION get_value_by_key
(
p_str VARCHAR2
, p_key VARCHAR2
, p_kvp_separator VARCHAR2
, p_kv_separator VARCHAR2
) RETURN VARCHAR2
AS
v_key VARCHAR2(32767);
v_value VARCHAR2(32767);
v_which NUMBER;
v_cur VARCHAR(1);
BEGIN
v_which := 0;
FOR i IN 1..length(p_str)
LOOP
v_cur := substr(p_str,i,1);
IF v_cur = p_kvp_separator
THEN
IF v_key = p_key
THEN
EXIT;
END IF;
v_key := '';
v_value := '';
v_which := 0;
ELSIF v_cur = p_kv_separator
THEN
v_which := 1;
ELSE
IF v_which = 0
THEN
v_key := v_key || v_cur;
ELSE
v_value := v_value || v_cur;
END IF;
END IF;
END LOOP;
IF v_key = p_key
THEN
RETURN v_value;
END IF;
raise_application_error(-20001, 'key not found!');
END;
To get the value for 'Key2' you could do this (assuming your function was in a package called test_pkg):
SELECT test_pkg.get_value_by_key('Key1,Value1|Key2,Value2|Key3,Value3','Key2','|',',') FROM dual