将此 CSV 文件加载到其中后,如何根据 Stringlist 中的索引访问单个记录。
CSV 示例:
Record0;Record1;Record2
Record0;Record1;Record2
Record0;Record1;Record2
Record0;Record1;Record2
将此 CSV 文件加载到其中后,如何根据 Stringlist 中的索引访问单个记录。
CSV 示例:
Record0;Record1;Record2
Record0;Record1;Record2
Record0;Record1;Record2
Record0;Record1;Record2
您不能将 TStringList 用于RFC4180中描述的 CSV 文件
有效 RFC4180 CSV 文件示例(2 行,5 字段)
Data1;"Data2;Data2";"Data3
Data3";"Data4""Data4""";Data5
Data1;"Data2;Data2";"Data3
Data3";"Data4""Data4""";Data5
和单个字段值
但也许你觉得这很有用(我在 2011 年写的一个快速示例)。
不要混淆,您可以修改单元格值但不能修改 SaveToFile 方法。
unit CSVData;
interface
type
TCSVData = class
private
FData : array of array of string;
FDelim : Char;
FQuote : Char;
function GetRows : Integer;
function GetCols : Integer;
function GetCell( Row, Col : Integer ) : string;
procedure SetCell( Row, Col : Integer; const Value : string );
public
destructor Destroy; override;
procedure LoadFromFile( const FileName : string );
property Cell[Row, Col : Integer] : string
read GetCell
write SetCell;
property Rows : Integer
read GetRows;
property Cols : Integer
read GetCols;
property Delim : Char
read FDelim
write FDelim;
property Quote : Char
read FQuote
write FQuote;
end;
implementation
uses
Classes;
{ TCSVData }
destructor TCSVData.Destroy;
begin
SetLength( FData, 0, 0 );
inherited;
end;
function TCSVData.GetCell( Row, Col : Integer ) : string;
begin
Result := FData[Row, Col];
end;
function TCSVData.GetCols : Integer;
begin
if Rows > 0
then
Result := Length( FData[0] )
else
Result := 0;
end;
function TCSVData.GetRows : Integer;
begin
Result := Length( FData );
end;
procedure TCSVData.LoadFromFile( const FileName : string );
var
Data : TStrings;
Val : string;
MyChar : Char;
LastChar : Char;
QuotePart : Boolean;
Col : Integer;
Row : Integer;
MaxCol : Integer;
begin
Data := TStringList.Create;
try
Data.LoadFromFile( FileName );
LastChar := #0;
QuotePart := False;
Val := '';
MaxCol := 0;
Col := 0;
Row := 0;
// Jedes Zeichen durchlaufen
for MyChar in Data.Text do
begin
if ( MyChar = Quote )
then
begin
// QuotePart wechselt den Status
QuotePart := not QuotePart;
// Befinden wir uns im QuotePart und das letzte Zeichen
// war das Quote-Zeichen, dann handelt es sich um eine
// Verdoppelung und wir hängen das Quote-Zeichen an
// den Puffer
if QuotePart and ( LastChar = Quote )
then
Val := Val + Quote;
end
else if not QuotePart and ( MyChar = Delim )
then
begin
// Sind noch nicht genug Zeilen da ...
if high( FData ) < Row + 1
then
// ... dann auf Verdacht schon mal 10 hinzufügen
SetLength( FData, Row + 10 );
// Sind noch nicht genug Spalten da ...
if high( FData[Row] ) < Col + 1
then
// ... dann auf Verdacht schon mal 10 hinzufügen
SetLength( FData[Row], Col + 10 );
// Wert eintragen
FData[Row, Col] := Val;
// Puffer leeren
Val := '';
// Spalte hochzählen
Inc( Col );
end
else if not QuotePart and ( ( MyChar = #13 ) or ( MyChar = #10 ) )
then
begin
// Haben wir CR LF gefunden ...
if ( MyChar = #10 ) and ( LastChar = #13 )
then
begin
// Sind noch nicht genug Zeilen da ...
if high( FData ) < Row + 1
then
// ... dann auf Verdacht schon mal 10 hinzufügen
SetLength( FData, Row + 10 );
// Die Anzahl der Spalten steht jetzt fest
SetLength( FData[Row], Col + 1 );
// MaxCol merken
if Col > MaxCol
then
MaxCol := Col;
// Wert eintragen
FData[Row, Col] := Val;
// Puffer leeren
Val := '';
// Zeile hochzählen
Inc( Row );
// Neue Zeile => erste Spalte
Col := 0;
end;
end
else
// Das aktuelle Zeichen an den Puffer hängen
Val := Val + MyChar;
// Das letzte Zeichen merken
LastChar := MyChar;
end;
SetLength( FData, Row );
// Das ist eigentlich nur notwendig, wenn die CSV-Datei falsch aufgebaut ist
// und unterschiedliche Anzahl von Spalten in den Zeilen aufweist
// Dieses ist allerdings nicht RFC-konform, aber wir wollen mal nicht so sein
for Row := low( FData ) to high( FData ) do
SetLength( FData[Row], MaxCol + 1 );
finally
Data.Free;
end;
end;
procedure TCSVData.SetCell( Row, Col : Integer; const Value : string );
begin
FData[Row, Col] := Value;
end;
end.
PS:我知道我有另一种使用状态模式的方法,但我找不到它......也许稍后
SplitString
将使用您定义的分隔符拆分您的字符串。在此示例中,空格和 ; 特点。
更新
添加了索引拆分函数的示例。( SplitByIndex
)。
更新 2
添加了一个示例 ( SplitByIndexAlt
) 不使用SplitString
, 但TStringList.DelimitedText
. 这将处理空格和 ; 作为分隔符(不是由 括起来的分隔符QuoteChar
)。
uses
SysUtils,Classes,System.Types,System.StrUtils;
procedure Test(aStringList: TStringList);
var
s,split : String;
splittedString : TStringDynArray;
begin
for s in aStringList do begin
splittedString := SplitString(s,' ;'); // Splits at space and ;
for split in splittedString do
begin
WriteLn(split);
end;
end;
end;
Function SplitByIndex(aList : TStringList; aRow,aCol : Integer) : String;
// Zero based index !
var
splittedString : TStringDynArray;
begin
Result := '';
if (aRow < aList.Count) then
begin
splittedString := SplitString(aList[aRow],' ;');
if (aCol < Length(splittedString))
then Result := splittedString[aCol];
end;
end;
Function SplitByIndexAlt(aList : TStringList; aRow,aCol : Integer) : String;
// Zero based index !
var
splitlist : TstringList;
begin
Result := '';
if (aRow < aList.Count) then
begin
splitList := TStringList.Create;
Try
splitList.Delimiter := ';';
// splitList.QuoteChar := '"'; // This may have to be changed
splitList.StrictDelimiter := false;
splitList.DelimitedText := aList[aRow];
if (aCol < splitList.Count)
then Result := splitList[aCol];
Finally
splitList.Free;
End;
end;
end;
var
myList: TStringList;
begin
myList := TStringList.Create;
Try
myList.Add('#0 Record0;Record1;Record2');
myList.Add('#1 Record0;Record1;Record2');
myList.Add('#2 Record0;Record1;Record2');
myList.Add('#3 Record0;Record1;Record2');
Test(myList);
WriteLn(SplitByIndex(myList,0,4);
ReadLn;
Finally
myList.Free;
End;
end.
此处的输出将如下所示:
#0
Record0
Record1
Record2
etc
现在考虑 CSV 文件格式未标准化,请参阅CSV Wiki。因此,对于更通用的解决方案,解决方案可能看起来更复杂。