hello guys I have the following string A type string.
A = 'flagA=0,flagB=0,flagC=1'
B:Tstringlist.
I want to convert it into Tstringlist in order to see if the flagC is set to 1 by inspecting B.Values['flagC'].
perhaps i need to split the string under '=' and ',' ?
Any other ideas are welcome thanks.
This should work
B := TStringList.Create;
B.Delimiter := ',';
B.DelimitedText := 'flagA=0,flagB=0,flagC=1';
ShowMessage(B.Values['flagC']);
cars.Free;
enter image description here
you can use that code
procedure TForm1.Button1Click(Sender: TObject);
var
b,a:string;
i:integer;
list:TStringList;
begin
list:=TStringList.Create;
a := 'flagA=0,flagB=0,flagC=1';
for i:=0 to length(a) do
if a[i]='f' then
begin
b := copy(a,i,7);
list.Add(b);
ListBox1.Clear;
ListBox1.Items.Assign(list);
end;
end;
you can use edit1 like this
a:= edit1.text //that consider the contain as string
Related
how to print all data/records contain in Recordset to the text file in Delphi 10 ?
I could not able to find any Method or property for it.Please guide, i am newbie in delphi.
I have done following:
Var:
CurrField : Field;
RecSet:_RecordSet ;
Begin:
RecSet:= command.Execute(records,Params,-1);
CurrField := RecSet.Fields[0];
end;
but i want to print complete records/data contain in RecSet(_RecordSet type) in text file.
You can write it yourself. If the recordset is relatively small, the easiest way is to use a TStringList.
var
i: Integer;
s: string;
SL: TStringList;
begin
SL := TStringList.Create;
try
while not RecSet.Eof do
begin
// Clear string for the next row
s := '';
// Loop through the fields in this row, creating a comma-separated list
for i := 0 to RecSet.FieldCount - 1 do
s := s + RecSet.Fields[i].Value + ',';
// Remove unnecessary final comma at end
SetLength(s, Length(s) - 1);
// Add to the stringlist
SL.Add(s);
end;
// Save the stringlist content to disk
SL.SaveToFile('YourFileName.txt');
finally
SL.Free;
end;
end;
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 want to add 30 different strings into a stringList . I do not want to add AList.Items.Add 30 times. Nor do i want to keep the strings in an array and run a loop. I was thinking may be i could write a single AList.Add ( not in a loop) where the strings to be added were seperated by a Delimiter .
e.g.
AList.Add('Data1' + <Delim> + 'Data2' ...)
How to do that ?
Please note that i am just curious as to if it can be done this way. It is quite ok if not as there are better ways to accomplish this. ( keeping the strings in an array and using a loop to add data is my idea)
Thanks in Advance
You can write a procedure that does this:
procedure SLAddStrings(SL: TStrings; S: array of string);
var
i: Integer;
begin
SL.BeginUpdate;
for i := low(S) to high(S) do
SL.Add(S[i]);
SL.EndUpdate;
end;
Try it:
var
SL: TStringList;
begin
SL := TStringList.Create;
SLAddStrings(SL, ['car', 'cat', 'dog']);
Create a temporary TStringList, assign the string to its DelimitedText property, pass the temporary to the AddStrings() method of the destination TStringList, and then free the temporary.
var
Temp: TStringList;
begin
Temp := TStringList.Create;
try
Temp.Delimiter := <Delim>;
// if using a Delphi version that has StrictDelimiter available:
// Temp.StrictDelimiter := True;
Temp.DelimitedText := 'Data1' + <Delim> + 'Data2' ...;
AList.AddStrings(Temp);
finally
Temp.Free;
end;
end;
Just use DelimitedText property. E.g. if your delimiter is set to , (default in TStringList) then you can write this code:
AList.DelimitedText := 'Data1,Data2';
you can use TStringList.DelimitedText property to add text, wich uses your Delimiter char. TStringList will split your text and then you can access each string separately using strings property;
program Project3;
{$APPTYPE CONSOLE}
uses classes;
const DATA = 'one,two,three';
var sl : TStringList;
s : string;
begin
sl := TStringList.Create();
try
sl.Delimiter := ',';
sl.DelimitedText := DATA;
for s in sl do begin
writeln(s);
end;
readln;
finally
sl.Free();
end;
end.
and result is
one
two
three
I want to export content of a TQuery to a CSV file without using a 3d part component(Delphi 7). From my knowledge this can not be accomplished with Delphi standard components.
My solution was to save the content in a StringList with a CSV format, and save it to a file.
Is there any comfortable solution?
PS:I don't want to use JvCsvDataSet or any component. Question is: can this be accomplished only with Delphi 7 or higher standard components?
Thank you in advance!
Of course it can.
You just have to do the work to properly output the CSV content (quoting properly, handling embedded quotes and commas, etc.). You can easily write the output using TFileStream, and get the data using the TQuery.Fields and TQuery.FieldCount properly.
I'll leave the fancy CSV quoting and special handling to you. This will take care of the easy part:
var
Stream: TFileStream;
i: Integer;
OutLine: string;
sTemp: string;
begin
Stream := TFileStream.Create('C:\Data\YourFile.csv', fmCreate);
try
while not Query1.Eof do
begin
// You'll need to add your special handling here where OutLine is built
OutLine := '';
for i := 0 to Query.FieldCount - 1 do
begin
sTemp := Query.Fields[i].AsString;
// Special handling to sTemp here
OutLine := OutLine + sTemp + ',';
end;
// Remove final unnecessary ','
SetLength(OutLine, Length(OutLine) - 1);
// Write line to file
Stream.Write(OutLine[1], Length(OutLine) * SizeOf(Char));
// Write line ending
Stream.Write(sLineBreak, Length(sLineBreak));
Query1.Next;
end;
finally
Stream.Free; // Saves the file
end;
end;
The original question asked for a solution using a StringList. So it would be something more like this. It will work with any TDataSet, not just a TQuery.
procedure WriteDataSetToCSV(DataSet: TDataSet, FileName: String);
var
List: TStringList;
S: String;
I: Integer;
begin
List := TStringList.Create;
try
DataSet.First;
while not DataSet.Eof do
begin
S := '';
for I := 0 to DataSet.FieldCount - 1 do
begin
if S > '' then
S := S + ',';
S := S + '"' + DataSet.Fields[I].AsString + '"';
end;
List.Add(S);
DataSet.Next;
end;
finally
List.SaveToFile(FileName);
List.Free;
end;
end;
You can add options to change the delimiter type or whatever.
This is like the Rob McDonell solution but with some enhancements: header, escape chars, enclosure only when required, and ";" separator.
You can easily disable this enhancements if not required.
procedure SaveToCSV(DataSet: TDataSet; FileName: String);
const
Delimiter: Char = ';'; // In order to be automatically recognized in Microsoft Excel use ";", not ","
Enclosure: Char = '"';
var
List: TStringList;
S: String;
I: Integer;
function EscapeString(s: string): string;
var
i: Integer;
begin
Result := StringReplace(s,Enclosure,Enclosure+Enclosure,[rfReplaceAll]);
if (Pos(Delimiter,s) > 0) OR (Pos(Enclosure,s) > 0) then // Comment this line for enclosure in every fields
Result := Enclosure+Result+Enclosure;
end;
procedure AddHeader;
var
I: Integer;
begin
S := '';
for I := 0 to DataSet.FieldCount - 1 do begin
if S > '' then
S := S + Delimiter;
S := S + EscapeString(DataSet.Fields[I].FieldName);
end;
List.Add(S);
end;
procedure AddRecord;
var
I: Integer;
begin
S := '';
for I := 0 to DataSet.FieldCount - 1 do begin
if S > '' then
S := S + Delimiter;
S := S + EscapeString(DataSet.Fields[I].AsString);
end;
List.Add(S);
end;
begin
List := TStringList.Create;
try
DataSet.DisableControls;
DataSet.First;
AddHeader; // Comment if header not required
while not DataSet.Eof do begin
AddRecord;
DataSet.Next;
end;
finally
List.SaveToFile(FileName);
DataSet.First;
DataSet.EnableControls;
List.Free;
end;
end;
Delphi does not provide any built-in access to .csv data.
However, following the VCL TXMLTransform paradigm, I wrote a TCsvTransform class helper that will translate a .csv structure to /from a TClientDataSet.
As for the initial question that was to export a TQuery to .csv, a simple TDataSetProvider will make the link between TQuery and TClientDataSet.
For more details about TCsvTransform, cf http://didier.cabale.free.fr/delphi.htm#uCsvTransform
I have an attribute called HistoryText in a object that is stored as a string.
I want to show all rows in a grid. I should be able to delete and edit rows in the grid.
The format is:
16.5.2003-$-12:09-$-anna-$-Organization created
2.6.2005-$-13:03-$-jimmy-$-Organization edited
19.12.2005-$-13:33-$-madeleine-$-Organization edited
So each row have 4 fields, date, time, user, and message with a delimiter string as '-$-'.
As the delimiter a string and not a char it cannot be assigned to the stringlists delimiter property.
I have a routine to extract the string to a Stringlist:
procedure ParseDelimited(const aStringList: TStringList; const aOrgList, aDelimiter: string);
var
vDelimiterPos : integer;
vPartialStr : string;
vRemaingTxt : string;
vDelimiterLength : integer;
begin
vDelimiterLength := Length(aDelimiter);
if (AnsiRightStr(aOrgList, Length(aDelimiter)) = aDelimiter) then
vRemaingTxt := aOrgList
else
vRemaingTxt := aOrgList + aDelimiter;
aStringList.BeginUpdate;
aStringList.Clear;
try
while Length(vRemaingTxt) > 0 do
begin
vDelimiterPos := Pos(aDelimiter, vRemaingTxt);
vPartialStr := Copy(vRemaingTxt,0,vDelimiterPos-1);
aStringList.Add(vPartialStr);
vRemaingTxt := Copy(vRemaingTxt,vDelimiterPos+vDelimiterLength,MaxInt);
end;
finally
aStringList.EndUpdate;
end;
end;
and it seems to work fine. My problem is syncing the changes in the StringList back to the original String property ? There are so much historical data with this delimiter so I don't think change it to a TChar is a realistic option.
Update:
A clarification. I think I can manage to convert the String to a StringList with the method above. Then display it in the grid should not be so hard. The problem come when I want to convert the TStringList back to the original String property wih '-$-' as delimiter. I cannot do HistoryText := myStringList.Delimitedtext for example.
Second update:
I have solved it. You all got a +1 for fast answers and really trying to help. In summary how I did it.
Read from Historytext:
MyStringList.Text := Historytext;
Now each row have 3 delimiters of '-$-' and each line is separated by a linefeed as usual.
In a loop parse the Stringlist and show it in the grid. I don't bother about MyStringList anymore.
Let the user delete and edit rows in the grid.
When finished loop by row and columns in the grid and build a new string with the same format as original.
Assign that string to HistoryText.
So shift focus from StringList to the grid made it easier :)
Instead of Delimiter (a char) and DelimitedText, you can also use LineBreak (a string) and Text:
lst := TStringList.Create;
try
lst.LineBreak := '-$-';
lst.Text := '16.5.2003-$-12:09-$-anna-$-Organization created';
Memo1.Lines := lst; // or whatever you do with it
finally
lst.Free;
end;
Ans it works even the other way round.
Wild stab in the dark (it isn't very clear what you are asking):
Work through the grid rows:
For each row:
assign an empty string to a temporary string var
For each column add row/column value plus your delimiter to temporary string var
remove last delimiter from temporary string var (if it is non-empty)
add temporary string var to stringlist
Write stringlist's text property back to your HistoryText
const
Delimiter = '-$-';
var
row: Integer;
col: Integer;
SL: TStringList;
rowString: string;
begin
SL := TStringList.Create;
try
for row := 0 to StringGrid1.RowCount - 1 do begin
rowString := '';
for col := 0 to StringGrid1.ColCount - 1 do begin
rowString := StringGrid1.Cells[col, row] + Delimiter;
end;
if rowString <> '' then begin
rowString := Copy(rowString, 1, Length(rowString) - Length(Delimiter));
end;
SL.Add(rowString);
end;
HistoryText := SL.Text;
finally
SL.Free;
end;
end;
Using Uwe's solution of TStrings' LineBreak property:
var
row: Integer;
col: Integer;
SLRows: TStringList;
SLCols: TStringlist;
begin
SLRows := TStringList.Create;
try
SLCols := TStringList.Create;
try
SLCols.LineBreak := '-$-';
for row := 0 to StringGrid1.RowCount - 1 do begin
SLCols.Clear;
for col := 0 to StringGrid1.ColCount - 1 do begin
SLCols.Add(StringGrid1.Cells[col, row]);
end;
SLRows.Add(SLCols.Text);
end;
HistoryText := SLRows.Text;
finally
SLCols.Free;
end;
finally
SLRows.Free;
end;
end;