mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-18 04:19:12 +02:00
TCustomStringGrid.LoadFromCSVStream: fix parsing of strings with leading or trailing
whitespace. As per RFC4180 this whitespace must be part of the field, even if the field is not quoted. git-svn-id: trunk@37598 -
This commit is contained in:
parent
c5983f8f13
commit
d78838b4b1
@ -10252,17 +10252,17 @@ end;
|
|||||||
|
|
||||||
procedure TCustomStringGrid.LoadFromCSVStream(AStream: TStream;
|
procedure TCustomStringGrid.LoadFromCSVStream(AStream: TStream;
|
||||||
ADelimiter: Char=','; WithHeader: boolean=true);
|
ADelimiter: Char=','; WithHeader: boolean=true);
|
||||||
//Note for developer:
|
|
||||||
// When TStrings.SetDelimitedText gets fixed (in a release version)
|
|
||||||
// wen can remove this helper procedure and use DelimitedText with Strictdelimiter := True
|
|
||||||
Procedure ParseDelimitedText(const AValue: string; const ADelimiter, AQuoteChar: Char; TS: TStrings);
|
Procedure ParseDelimitedText(const AValue: string; const ADelimiter, AQuoteChar: Char; TS: TStrings);
|
||||||
{ Helper function for LoadFromCSVFile
|
{ Helper function for LoadFromCSVFile
|
||||||
Adapted from TStrings.SetDelimitedText
|
Adapted from TStrings.SetDelimitedText
|
||||||
- Only ADelimiter is used for separating the fields and not other whitespace
|
- Only ADelimiter is used for separating the fields and not other whitespace
|
||||||
- If a field is quoted and it contains AQuoteChar, this occurrence is treated as a literal part of the field
|
- If a field is quoted and it contains AQuoteChar, this occurrence is treated as a literal part of the field
|
||||||
|
- As per RFC4180 whitespace is considered to be part of the field, even if the field is not quoted
|
||||||
|
- Trailing spaces of a quoted field are trimmed
|
||||||
Example with ADelimiter = ',' and AQuoteChar = '"'
|
Example with ADelimiter = ',' and AQuoteChar = '"'
|
||||||
AValue = '111,2,22,333' -> 111|2|22|333
|
AValue = '111,2,22,333' -> 111|2|22|333
|
||||||
AValue = '111,"2,22",333' -> 111|2,22|333
|
AValue = '111,"2,22",333' -> 111|2,22|333
|
||||||
|
AValue = '111, 222 ,333' -> 111| 222 |333
|
||||||
}
|
}
|
||||||
var i,j:integer;
|
var i,j:integer;
|
||||||
aNotFirst:boolean;
|
aNotFirst:boolean;
|
||||||
@ -10277,8 +10277,6 @@ procedure TCustomStringGrid.LoadFromCSVStream(AStream: TStream;
|
|||||||
begin
|
begin
|
||||||
// skip delimiter
|
// skip delimiter
|
||||||
if aNotFirst and (i<=length(AValue)) and (AValue[i]=ADelimiter) then inc(i);
|
if aNotFirst and (i<=length(AValue)) and (AValue[i]=ADelimiter) then inc(i);
|
||||||
// skip spaces
|
|
||||||
while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
|
|
||||||
// read next string
|
// read next string
|
||||||
if i<=length(AValue) then
|
if i<=length(AValue) then
|
||||||
begin
|
begin
|
||||||
@ -10305,10 +10303,10 @@ procedure TCustomStringGrid.LoadFromCSVStream(AStream: TStream;
|
|||||||
// next string is not quoted
|
// next string is not quoted
|
||||||
j:=i;
|
j:=i;
|
||||||
while (j<=length(AValue)) and
|
while (j<=length(AValue)) and
|
||||||
(Ord(AValue[j])>=Ord(' ')) and //spaces are not treated as delimiter
|
//basically any other character means some invalid text
|
||||||
|
((Ord(AValue[j])>=Ord(' ')) or (AValue[j] in [#10,#13,#9,#0])) and
|
||||||
(AValue[j]<>ADelimiter) do inc(j);
|
(AValue[j]<>ADelimiter) do inc(j);
|
||||||
//We may have trailing spaces, these need to be trimmed
|
TS.Add(Copy(AValue,i,j-i));
|
||||||
TS.Add( TrimRight(Copy(AValue,i,j-i)));
|
|
||||||
i:=j;
|
i:=j;
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
@ -10316,8 +10314,9 @@ procedure TCustomStringGrid.LoadFromCSVStream(AStream: TStream;
|
|||||||
begin
|
begin
|
||||||
if aNotFirst then TS.Add('');
|
if aNotFirst then TS.Add('');
|
||||||
end;
|
end;
|
||||||
// skip spaces
|
// skip trailing spaces of a quoted field
|
||||||
while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
|
// not really sure if that is RFC4180 compliant
|
||||||
|
while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) and (AValue[i] <> ADelimiter) do inc(i);
|
||||||
aNotFirst:=true;
|
aNotFirst:=true;
|
||||||
end; //end of string
|
end; //end of string
|
||||||
finally
|
finally
|
||||||
@ -10418,7 +10417,7 @@ begin
|
|||||||
HeaderL.Add(c.Title.Caption);
|
HeaderL.Add(c.Title.Caption);
|
||||||
end;
|
end;
|
||||||
HeaderL.Delimiter:=ADelimiter;
|
HeaderL.Delimiter:=ADelimiter;
|
||||||
Headerl.StrictDelimiter := False; //do not rely on default value here
|
Headerl.StrictDelimiter := False; //force quoting of strings that contain whitespace or Delimiter
|
||||||
Lines.Add(HeaderL.DelimitedText); // Add as a first row in Lines
|
Lines.Add(HeaderL.DelimitedText); // Add as a first row in Lines
|
||||||
finally
|
finally
|
||||||
HeaderL.Free;
|
HeaderL.Free;
|
||||||
@ -10433,7 +10432,7 @@ begin
|
|||||||
end else
|
end else
|
||||||
StartRow := FixedRows;
|
StartRow := FixedRows;
|
||||||
for i:=StartRow to RowCount-1 do begin
|
for i:=StartRow to RowCount-1 do begin
|
||||||
Rows[i].StrictDelimiter := False; //do not rely on default value here
|
Rows[i].StrictDelimiter := False; //force quoting of strings that contain whitespace or Delimiter
|
||||||
Rows[i].Delimiter:=ADelimiter;
|
Rows[i].Delimiter:=ADelimiter;
|
||||||
Lines.Add(Rows[i].DelimitedText);
|
Lines.Add(Rows[i].DelimitedText);
|
||||||
end;
|
end;
|
||||||
|
Loading…
Reference in New Issue
Block a user