I have string 'AAA'+#$0d+#$0a+'BBB'+#$01d+'CCC'. I need to split according #$1d character like:
'AAA'+#$0d+#$0a+'BBB'
'CCC'
I'm using function:
procedure Split(Delimiter: Char; Str: string; ListOfStrings: TStrings) ;
begin
ListOfStrings.Clear;
ListOfStrings.Delimiter := Delimiter;
ListOfStrings.DelimitedText := Str;
ListOfStrings.StrictDelimiter:= true;
end;
...
split(#$1d,'AAA'+#$0d+#$0a+'BBB'+#$01d+'CCC',sl);
Unfortunately it splits also according CRLF that I don't need.
How to have strings spited just by #$1d?
You have to set ListOfStrings.StrictDelimiter:= true; before setting the of the property DelimitedText.
ListOfStrings.StrictDelimiter:= true;
ListOfStrings.DelimitedText := Str;
Related
I have a string list, each string looks something like this.
2023/01/30,08:47:27, 0. 7.71,CM212-A2,03,Bad head/nozzle detect,380000,Stage No1, Head No2, Nozzle Postion5, Nozzle No0, (Single Lane), PCB ID: , Bad head/nozzle detectReset
I am then adding that string into another list with commatext, and should be giveing a result like so
2023/01/30
08:47:27
0. 7.71
CM212-A2
03
Bad head/nozzle detect
Instead it returns
2023/01/30
08:47:27
0.
7.71
CM212-A2
03
Bad
head/nozzle
detect
It seems it will create a new string for each Comma like I want, but also is creating a new string for each space which I don't want.
Adding Code just incase its needed.
procedure TForm4.Button1Click(Sender: TObject);
var
datalist : tstringlist;
eventlist : tstringlist;
i,x: Integer;
filename : string;
Event : array[1..200] of TEvent;
begin
datalist := tstringlist.Create;
eventlist := tstringlist.Create;
X := 1;
//get latest file
filename := getlastmodifiedfilename('C:\Users\tngmorse\Desktop\LockHeads\Win32\Debug\');
//end
datalist.LoadFromFile(filename);
//search for head lockout
for i := 0 to datalist.count - 1 do
begin
if containstext(datalist[i],'Bad head') then
begin
memo1.Lines.Add(datalist[i]);
// eventlist.Clear;
eventlist.CommaText := datalist[i];
// Event[x].Dateofevent := 'today';
Event[x].Dateofevent := eventlist[0]; //spaces are also breaking it.
Event[x].Timeofevent := eventlist[1];
Event[x].Version := eventlist[2];
Event[x].Machine := Eventlist[3];
Event[x].EventNumber := Eventlist[4];
Event[x].EventName := Eventlist[5];
Event[x].TableNumber := EventList[6];
Event[x].Table := Eventlist[7];
Event[x].Head := Eventlist[8];
Event[x].Nozzle := EventList[9];
Event[x].NozzleType := Eventlist[10];
Event[x].Lane := Eventlist[11];
Event[x].PCBID := Eventlist[12];
Event[x].Effect := Eventlist[13];
memo1.Lines.Add('Date: '+Event[x].Dateofevent);
memo1.Lines.Add('Time: '+Event[x].Timeofevent);
memo1.Lines.Add('Machine: '+Event[x].Machine);
memo1.Lines.Add('Event: '+Event[x].EventName);
memo1.Lines.Add('Table: '+Event[x].Table);
memo1.Lines.Add('Head: '+Event[x].Head);
memo1.Lines.Add('Nozzle: '+Event[x].Nozzle);
memo1.Lines.Add('Status: '+Event[x].Effect);
x:=x+1;
//break down list by comma
//add to record
end;
end;
//end
showmessage('lines: '+inttostr(datalist.Count));
datalist.Destroy;
end;
This behavior can be controlled by the StrictDelimiter property:
If StrictDelimiter is True, individual strings in DelimitedText are only separated by Delimiter or quoted between QuoteChar. If StrictDelimiter is False, spaces and non-printable character are also used as delimiters.
So, set it to True to get the desired behavior.
I have a Tstringlist containing a list of keys used in a database table.
I'd like a simple way to generate one string containing all the keys, with each separated by a comma and enclosed in single quotes.
This is so that it can be used in a SQL 'IN' statement
eg WHERE FieldX IN ('One','Two','Three').
I've tried using quotechar but it is ignored when reading the commatext.
eg the following code
procedure junk;
var
SL : Tstringlist;
s : string;
begin
SL := Tstringlist.Create;
SL.Delimiter :=','; //comma delimiter
SL.QuoteChar := ''''; //single quote around strings
SL.Add('One');
SL.Add('Two');
SL.Add('Three');
try
s := SL.commatext;
showmessage(s);
finally
SL.Free;
end; //finally
end; //junk
shows the message One,Two,Three - without any quotes.
I know I can do it the long way round, as in
procedure junk;
var
SL : Tstringlist;
s : string;
i : integer;
begin
SL := Tstringlist.Create;
SL.Delimiter :=','; //comma delimiter
SL.Add('One');
SL.Add('Two');
SL.Add('Three');
try
s := '';
for I := 0 to SL.Count - 1 do
begin
s := s + ',' + '''' + SL[i] + '''';
end;
delete(s,1,1);
showmessage(s);
finally
SL.Free;
end;//finally
end;
but is there a simpler way using properties of the Tstringlist itself?
If you're using D2006 or later, you can use a CLASS HELPER:
USES Classes,StrUtils;
TYPE
TStringListHelper = CLASS HELPER FOR TStrings
FUNCTION ToSQL : STRING;
END;
FUNCTION TStringListHelper.ToSQL : STRING;
VAR
S : STRING;
FUNCTION QuotedStr(CONST S : STRING) : STRING;
BEGIN
Result:=''''+ReplaceStr(S,'''','''''')+''''
END;
BEGIN
Result:='';
FOR S IN Self DO BEGIN
IF Result='' THEN Result:='(' ELSE Result:=Result+',';
Result:=Result+QuotedStr(S)
END;
IF Result<>'' THEN Result:=Result+')'
END;
This code:
SL:=TStringList.Create;
SL.Add('One');
SL.Add('Two');
SL.Add('Number Three');
SL.Add('It''s number 4');
WRITELN('SELECT * FROM TABLE WHERE FIELD IN '+SL.ToSQL);
will then output:
SELECT * FROM TABLE WHERE FIELD IN ('One','Two','Number Three','It''s number 4')
Use sl.DelimitedText instead of sl.CommaText to make it follow your settings. CommaText will temporarily change the Delimiter and QuoteChar to some hardcoded values.
CommaText (and apparently also DelimitedText) can not be relied on to add the quotes, because they treat single word and multi word strings differently.
When retrieving CommaText, any string in the list that include spaces,
commas or quotes will be contained in double quotes, and any double
quotes in a string will be repeated.
There doesn't seem to be any combination with the TStringList alone, so I suggest you add the strings using QuotedStr.
Following settings work as you wish, regardless of, whether strings are single words or multi words:
SL := Tstringlist.Create;
SL.Delimiter :=','; //comma delimiter
SL.StrictDelimiter := True;
// SL.QuoteChar := ''''; //single quote around strings
// SL.Add('One');
// SL.Add('Two words');
// SL.Add('Three');
SL.Add(QuotedStr('One'));
SL.Add(QuotedStr('Two words'));
SL.Add(QuotedStr('Three'));
s := SL.CommaText; // or SL.DelimitedText;
showmessage(s);
Output is:
'One','Two words','Three'
The TStrings.DelimitedText methods quote only the strings when needed: when there's something link O'ne this will print as 'O''ne' - i.e. it double the quotes.
You can achieve what you want setting a convenience delimiter and the QuoteChar property to the #0 value and then replacing the delimiters with the quotes.
procedure junk;
var
sl : Tstringlist;
s: string;
begin
sl := Tstringlist.Create;
try
sl.Delimiter := '|';
sl.QuoteChar := #0;//no quote
sl.Add('On''e');
sl.Add('Two');
sl.Add('Three');
//escape the single quotes
s := StringReplace(sl.DelimitedText, '''', '''''', [rfReplaceAll]);
//replace the delimiters and quote the text
s := '''' + StringReplace(s, sl.Delimiter, ''',''', [rfReplaceAll]) + '''';
WriteLn(s);
finally
sl.Free;
end;
end;
I have resolved a similar issue with something like that :
s := ''' + StringReplace(sl.CommaText, ',', ''',''', [rfReplaceAll]) + '''
i need to extract string from a text as following example
Hi i have no name <z>empty</z>
i wanted to extract only text before <z> into array or string which is hi i have no name
i tried this function
procedure Split (const Delimiter: Char; Input: string; const Strings: TStrings);
begin
Assert(Assigned(Strings)) ;
Strings.Clear;
Strings.StrictDelimiter := true;
Strings.Delimiter := Delimiter;
Strings.DelimitedText := Input;
end;
but its only can split chars like ;,: etc.. i wanted to start split with this specific string <z>
As I read what you have written, you have a string and you want to ignore all text after the first occurrence of <z>. Use Pos and Copy for instance:
P := Pos('<z>', input);
if P = 0 then
output := input
else
output := Copy(input, 1, P-1);
Although something tells me that you really want an XML parser.
Just an example using the string helper routines:
function CutString(const input,pattern: String): String;
var
p: Integer;
begin
p := input.IndexOf(pattern);
if (p >= 0) then
Result := input.Substring(0,p)
else
Result := input;
end;
var
s: string;
begin
s := 'Hi i have no name<z>empty</z>';
s := CutString(s,'<z>');
WriteLn(s); // Outputs: 'Hi i have no name'
end.
Here is a solution for the one-liner-fan, the crack-nuts-with-sledgehammer-lover, the regex-enthusiast, the waste-performance-ignorant and the xml-parser-grump:
TRegEx.Replace('Hi i have no name <z>empty</z>', '((^.*)(?=<z>)|(^.*)(?!<z>)).*', '$1');
If the delimiter will be there for sure, you can do:
Result:= Copy(S, 1, Pos(Delimiter, S) - 1);
Otherwise user Davids answer or this (shorter but bad performance):
Result:= Copy(S, 1, Pos(Delimiter, S + Delimiter) - 1);
Which is the best method to detect if a string is Base64Encoded or not (using Delphi)?
Best you can do is try to decode it. If the decode fails then the input was not base64 encoded. It the string successfully decodes then the input might have been base64 encoded.
You can check if the string only contains Base64 valids chars
function StringIsBase64(const InputString : String ) : Boolean;
const
Base64Chars: Set of AnsiChar = ['A'..'Z','a'..'z','0'..'9','+','/','='];
var
i : integer;
begin
Result:=True;
for i:=1 to Length(InputString) do
{$IFDEF UNICODE}
if not CharInSet(InputString[i],Base64Chars) then
{$ELSE}
if not (InputString[i] in Base64Chars) then
{$ENDIF}
begin
Result:=False;
break;
end;
end;
The = char is used for padding so you can add an aditional valiation to the function for padded base64 strings checking if the length of the string is mod 4
In addition to RRUZ answer you can also check the length of the string (is it a multiple of 4).
function IsValidBase64(const aValue: string): Boolean;
var
i: Integer;
lValidChars: set of Char;
begin
Result := aValue <> '';
lValidChars := ['a'..'z', 'A'..'Z', '0'..'9', '/', '+'];
//length of string should be multiple of 4
if Length(aValue) mod 4 > 0 then
Result := False
else
for i := 1 to Length(aValue) do
begin
if aValue[i] = '=' then
begin
if i < Length(aValue) - 1 then
begin
Result := False;
Exit;
end
else
lValidChars := ['='];
end
else if not (aValue[i] in lValidChars) then
begin
Result := False;
Break;
end;
end;
end;
Please note that this code is Delphi 7 code and not adjusted for Unicode use.
As was already told here, there is no reliable verification if a certain string is Base64 encoded or not, so even when you consider the input as a valid Base64 encoded string, it doesn't mean the string is actually encoded that way. I'm posting here just another version of a validation function, which according to RFC 4648 verifies:
if the input string is not empty and its length is multiple of 4
if the input string contains at most two padding characters and only at the end of the string
if the input string contains only characters from the Base64 alphabet (see the Page 5, Table 1)
function IsValidBase64EncodedString(const AValue: string): Boolean;
const
Base64Alphabet = ['A'..'Z', 'a'..'z', '0'..'9', '+', '/'];
var
I: Integer;
ValLen: Integer;
begin
ValLen := Length(AValue);
Result := (ValLen > 0) and (ValLen mod 4 = 0);
if Result then
begin
while (AValue[ValLen] = '=') and (ValLen > Length(AValue) - 2) do
Dec(ValLen);
for I := ValLen downto 1 do
if not (AValue[I] in Base64Alphabet) then
begin
Result := False;
Break;
end;
end;
end;
Is there a method in Delphi to check if a string is a number without raising an exception?
its for int parsing.
and an exception will raise if one use the
try
StrToInt(s);
except
//exception handling
end;
function TryStrToInt(const S: string; out Value: Integer): Boolean;
TryStrToInt converts the string S, which represents an integer-type number in either decimal or hexadecimal notation, into a number, which is assigned to Value. If S does not represent a valid number, TryStrToInt returns false; otherwise TryStrToInt returns true.
To accept decimal but not hexadecimal values in the input string, you may use code like this:
function TryDecimalStrToInt( const S: string; out Value: Integer): Boolean;
begin
result := ( pos( '$', S ) = 0 ) and TryStrToInt( S, Value );
end;
var
s: String;
iValue, iCode: Integer;
...
val(s, iValue, iCode);
if iCode = 0 then
ShowMessage('s has a number')
else
ShowMessage('s has not a number');
Try this function StrToIntDef()
From help
Converts a string that represents an integer (decimal or hex notation) to a number with error default.
Pascal
function StrToIntDef(const S: string; Default: Integer): Integer;
Edit
Just now checked the source of TryStrToInt() function in Delphi 2007. If Delphi 7 dont have this function you can write like this. Its just a polished code to da-soft answer
function TryStrToInt(const S: string; out Value: Integer): Boolean;
var
E: Integer;
begin
Val(S, Value, E);
Result := E = 0;
end;
XE4 and newer:
for ch in s do
TCharacter.IsNumber(ch);
Don't forget:
uses System.Character
In delphi 7 you can use the Val procedure. From the help:
Unit: System
Delphi syntax: procedure Val(S; var V; var Code: Integer);
S is a string-type expression; it must be a sequence of characters that form a signed real number.
V is an integer-type or real-type variable. If V is an integer-type variable, S must form a whole number.
Code is a variable of type Integer.
If the string is invalid, the index of the offending character is stored in Code; otherwise, Code is set to zero. For a null-terminated string, the error position returned in Code is one larger than the actual zero-based index of the character in error.
use this function
function IsNumber(N : String) : Boolean;
var
I : Integer;
begin
Result := True;
if Trim(N) = '' then
Exit(False);
if (Length(Trim(N)) > 1) and (Trim(N)[1] = '0') then
Exit(False);
for I := 1 to Length(N) do
begin
if not (N[I] in ['0'..'9']) then
begin
Result := False;
Break;
end;
end;
end;
For older Delphi versions from delphi 5 help example:
uses Dialogs;
var
I, Code: Integer;
begin
{ Get text from TEdit control }
Val(Edit1.Text, I, Code);
{ Error during conversion to integer? }
if Code <> 0 then
MessageDlg('Error at position: ' + IntToStr(Code), mtWarning, [mbOk], 0);
else
Canvas.TextOut(10, 10, 'Value = ' + IntToStr(I));
end;
In some languages decimal separators are different (for example, '.' is used in English and ',' is used in Russian). For these cases to convert string to real number the following procedure is proposed:
function TryStrToFloatMultiLang(const S : String; out Value : Extended) : Boolean;
var
dc : char;
begin
Result := false;
dc := DecimalSeparator;
DecimalSeparator := '.';
try
Result := TryStrToFloat(S, Value);
except
DecimalSeparator := ',';
Result := TryStrToFloat(S, Value);
end;
DecimalSeparator := dc;
end;
Update
As #Pep mentioned TryStrToFloat catch exceptions, but it returns boolean value. So the correct code is:
function TryStrToFloatMultiLang(const S : String; out Value : Extended) : Boolean;
var
dc : char;
begin
Result := false;
dc := DecimalSeparator;
DecimalSeparator := '.';
Result := TryStrToFloat(S, Value);
if not Result then begin
DecimalSeparator := ',';
Result := TryStrToFloat(S, Value);
end;
DecimalSeparator := dc;
end;
When you using procedure
val(s, i, iCode);
and set value xd ....
val('xd', i, iCode)
as a result we obtain: 13
standard unit Variants
function VarIsNumeric(v:Variant):Boolean