我如何解析自定义格式的数据?
How could I parse data coming in a custom format?
有人可以帮我在 Delphi 中解析这个吗?我正在接收以下格式的数据:
Data = {
["Node 1 Name"] = {
["Item 1 Name"] = {"short name", "Description here, which could include [] brackets"},
["Item 2 Name"] = {"short name", "Description here, which could include [] brackets"},
},
["Node 2 Name"] = {
["Item 1 Name"] = {"short name", "Description here, which could include [] brackets"},
["Item 2 Name"] = {"short name", "Description here, which could include [] brackets"},
},
["Node 3 Name"] = {
["Item 1 Name"] = {"short name", "Description here, which could include [] brackets"},
["Item 2 Name"] = {"short name", "Description here, which could include [] brackets"},
}
}
基本上,我需要能够获取所有节点(它们的名称),以及与每个节点关联的所有项目名称及其短名称和描述。项目和节点的数量可以是可变的。这是最终结构,数据不能有更多的级别或值。
我为您在问题中显示的数据设计了一个非常简单的解析器。
是在他自己的单元里我给demo起名为ParseCustomDataParser
要使用它,请在源代码(可能是 TForm)的 uses 子句中添加 ParseCustomDataParser。然后你可以像这样使用它:
procedure TForm1.Button1Click(Sender: TObject);
const
SampleData : String =
'Data = {' +
' ["Node 1 Name"] = {' +
' ["Item 1 Name"] = {"short name 1/1", "Description here, which could include [] brackets"},' +
' ["Item 2 Name"] = {"short name 1/2", "Description here, which could include [] brackets"},' +
' },' +
' ["Node 2 Name"] = {' +
' ["Item 1 Name"] = {"short name 2/1", "Description here, which could include [] brackets"},' +
' ["Item 2 Name"] = {"short name 2/2", "Description here, which could include [] brackets"},' +
' },' +
' ["Node 3 Name"] = {' +
' ["Item 1 Name"] = {"short name 3/1", "Description here, which could include [] brackets"},' +
' ["Item 2 Name"] = {"short name 3/2", "Description here, which could include [] brackets"},' +
' }' +
'}';
var
Index : Integer;
NRow : Integer;
NItem : Integer;
CustomData : TCustomData;
begin
// Parse the source of data
try
Index := 1;
CustomData.Parse(SampleData, Index);
except
on E:Exception do begin
Memo1.Lines.Add(E.Message);
Exit;
end;
end;
// Display the parsed result
Memo1.Lines.Add(CustomData.Name);
for NRow := 0 to High(CustomData.Rows) do begin
Memo1.Lines.Add(' ' + CustomData.Rows[NRow].Name);
for NItem := 0 to High(CustomData.Rows[NRow].Items) do begin
Memo1.Lines.Add(
' Name="' + CustomData.Rows[NRow].Items[NItem].ShortName + '"' +
' Description="' + CustomData.Rows[NRow].Items[NItem].Description + '"');
end;
end;
end;
解析器将字符串作为数据源,并将索引作为字符串的起始位置(可能应该为 1)。
解析是作为包含结果的记录 TCustomData
的方法实现的。该记录包含一组行,每行由一组项目组成。
我对格式做了一些假设,因为你只是展示了一个例子,但没有完整的规范。例如,为了简单起见,我假设双引号中的字符串不包含字符双引号本身。另一个例子是,您的样本数据在项目列表的末尾有一个额外的逗号,而 不应该是一个。如果存在的话,我只是忽略了额外的昏迷。要根据我的代码制作真实世界的代码,您还应该注意许多其他细节。鉴于没有规范,这是我能做的最好的。你开始了。现在由您根据我的代码构建您的解决方案。
解析器源代码:
unit ParseCustomDataParser;
interface
uses
System.SysUtils;
const
UnexpectedDelimiter = 'Unexpected delimiter at index %d "%s", ' +
'should be "%s"';
type
TCustomDataItem = record
Name : String;
ShortName : String;
Description : String;
procedure Clear;
procedure Parse(const Src : String; var Index : Integer);
end;
TCustomDataRow = record
Name : String;
Items : array of TCustomDataItem;
procedure Clear;
procedure Parse(const Src : String; var Index : Integer);
end;
TCustomData = record
Name : String;
Rows : array of TCustomDataRow;
procedure Clear;
procedure Parse(const Src : String; var Index : Integer);
end;
implementation
{ Support routines }
procedure SkipSpaces(const Src : String; var Index : Integer);
begin
while (Index <= Length(Src)) and
(CharInSet(Src[Index], [' ', #9, #10, #13])) do
Inc(Index);
end;
function GetNextDelimiter(const Src : String; var Index : Integer) : Char;
begin
SkipSpaces(Src, Index);
if Index > Length(Src) then
Result := #0
else begin
Result := Src[Index];
Inc(Index);
end;
end;
function GetNextToken(const Src : String; var Index : Integer) : String;
var
N : Integer;
begin
SkipSpaces(Src, Index);
N := Index;
while (Index <= Length(Src)) and
(CharInSet(Src[Index], ['a'..'z', 'A'..'Z'])) do
Inc(Index);
Result := Copy(Src, N, Index - N);
end;
function GetNextQuotedString(const Src : String; var Index : Integer) : String;
var
Delimiter : Char;
N : Integer;
begin
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '"' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '"']);
N := Index;
// Find closing double quote
while (Index <= Length(Src)) and (Src[Index] <> '"') do
Inc(Index);
Result := Copy(Src, N, Index - N);
if (Index <= Length(Src)) and (Src[Index] = '"') then
Inc(Index); // Skip closing double quote
end;
{ TCustomData }
procedure TCustomData.Clear;
begin
Name := '';
SetLength(Rows, 0);
end;
procedure TCustomData.Parse(const Src: String; var Index : Integer);
var
Token : String;
Delimiter : Char;
NRow : Integer;
begin
Clear;
while Index <= Length(Src) do begin
Token := GetNextToken(Src, Index);
if Token = '' then
break;
Name := Token;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '=' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '=']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '{' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '{']);
while TRUE do begin
// Add a new row
NRow := Length(Rows);
SetLength(Rows, NRow + 1);
Rows[NRow].Parse(Src, Index);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '}' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '{']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ',' then begin
Dec(Index);
break;
end;
end;
end;
end;
{ TCustomDataRow }
procedure TCustomDataRow.Clear;
begin
Name := '';
SetLength(Items, 0);
end;
procedure TCustomDataRow.Parse(const Src: String; var Index : Integer);
var
Delimiter : Char;
QuotedString : String;
NRow : Integer;
begin
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '[' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '[']);
QuotedString := GetNextQuotedString(Src, Index);
if QuotedString = '' then
raise Exception.CreateFmt('Missing quoted string at index %d', [Index]);
Name := QuotedString;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ']' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, ']']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '=' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '=']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '{' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '{']);
while TRUE do begin
NRow := Length(Items);
SetLength(Items, NRow + 1);
Items[NRow].Parse(Src, Index);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ',' then begin
Dec(Index); // Go back to the delimiter
break;
end;
// Last item is allowed to have an extra coma
Delimiter := GetNextDelimiter(Src, Index);
Dec(Index); // Go back to the delimiter
if Delimiter <> '[' then
break;
end;
end;
{ TCustomDataItem }
procedure TCustomDataItem.Clear;
begin
Name := '';
ShortName := '';
Description := '';
end;
procedure TCustomDataItem.Parse(const Src: String; var Index: Integer);
var
Delimiter : Char;
QuotedString : String;
begin
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '[' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '[']);
QuotedString := GetNextQuotedString(Src, Index);
if QuotedString = '' then
raise Exception.CreateFmt('Missing quoted string at index %d', [Index]);
Name := QuotedString;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ']' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, ']']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '=' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '=']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '{' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '{']);
QuotedString := GetNextQuotedString(Src, Index);
if QuotedString = '' then
raise Exception.CreateFmt('Missing quoted string at index %d', [Index]);
ShortName := QuotedString;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ',' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, ',']);
QuotedString := GetNextQuotedString(Src, Index);
if QuotedString = '' then
raise Exception.CreateFmt('Missing quoted string at index %d', [Index]);
Description := QuotedString;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '}' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '}']);
end;
end.
有人可以帮我在 Delphi 中解析这个吗?我正在接收以下格式的数据:
Data = {
["Node 1 Name"] = {
["Item 1 Name"] = {"short name", "Description here, which could include [] brackets"},
["Item 2 Name"] = {"short name", "Description here, which could include [] brackets"},
},
["Node 2 Name"] = {
["Item 1 Name"] = {"short name", "Description here, which could include [] brackets"},
["Item 2 Name"] = {"short name", "Description here, which could include [] brackets"},
},
["Node 3 Name"] = {
["Item 1 Name"] = {"short name", "Description here, which could include [] brackets"},
["Item 2 Name"] = {"short name", "Description here, which could include [] brackets"},
}
}
基本上,我需要能够获取所有节点(它们的名称),以及与每个节点关联的所有项目名称及其短名称和描述。项目和节点的数量可以是可变的。这是最终结构,数据不能有更多的级别或值。
我为您在问题中显示的数据设计了一个非常简单的解析器。
是在他自己的单元里我给demo起名为ParseCustomDataParser
要使用它,请在源代码(可能是 TForm)的 uses 子句中添加 ParseCustomDataParser。然后你可以像这样使用它:
procedure TForm1.Button1Click(Sender: TObject);
const
SampleData : String =
'Data = {' +
' ["Node 1 Name"] = {' +
' ["Item 1 Name"] = {"short name 1/1", "Description here, which could include [] brackets"},' +
' ["Item 2 Name"] = {"short name 1/2", "Description here, which could include [] brackets"},' +
' },' +
' ["Node 2 Name"] = {' +
' ["Item 1 Name"] = {"short name 2/1", "Description here, which could include [] brackets"},' +
' ["Item 2 Name"] = {"short name 2/2", "Description here, which could include [] brackets"},' +
' },' +
' ["Node 3 Name"] = {' +
' ["Item 1 Name"] = {"short name 3/1", "Description here, which could include [] brackets"},' +
' ["Item 2 Name"] = {"short name 3/2", "Description here, which could include [] brackets"},' +
' }' +
'}';
var
Index : Integer;
NRow : Integer;
NItem : Integer;
CustomData : TCustomData;
begin
// Parse the source of data
try
Index := 1;
CustomData.Parse(SampleData, Index);
except
on E:Exception do begin
Memo1.Lines.Add(E.Message);
Exit;
end;
end;
// Display the parsed result
Memo1.Lines.Add(CustomData.Name);
for NRow := 0 to High(CustomData.Rows) do begin
Memo1.Lines.Add(' ' + CustomData.Rows[NRow].Name);
for NItem := 0 to High(CustomData.Rows[NRow].Items) do begin
Memo1.Lines.Add(
' Name="' + CustomData.Rows[NRow].Items[NItem].ShortName + '"' +
' Description="' + CustomData.Rows[NRow].Items[NItem].Description + '"');
end;
end;
end;
解析器将字符串作为数据源,并将索引作为字符串的起始位置(可能应该为 1)。
解析是作为包含结果的记录 TCustomData
的方法实现的。该记录包含一组行,每行由一组项目组成。
我对格式做了一些假设,因为你只是展示了一个例子,但没有完整的规范。例如,为了简单起见,我假设双引号中的字符串不包含字符双引号本身。另一个例子是,您的样本数据在项目列表的末尾有一个额外的逗号,而 不应该是一个。如果存在的话,我只是忽略了额外的昏迷。要根据我的代码制作真实世界的代码,您还应该注意许多其他细节。鉴于没有规范,这是我能做的最好的。你开始了。现在由您根据我的代码构建您的解决方案。
解析器源代码:
unit ParseCustomDataParser;
interface
uses
System.SysUtils;
const
UnexpectedDelimiter = 'Unexpected delimiter at index %d "%s", ' +
'should be "%s"';
type
TCustomDataItem = record
Name : String;
ShortName : String;
Description : String;
procedure Clear;
procedure Parse(const Src : String; var Index : Integer);
end;
TCustomDataRow = record
Name : String;
Items : array of TCustomDataItem;
procedure Clear;
procedure Parse(const Src : String; var Index : Integer);
end;
TCustomData = record
Name : String;
Rows : array of TCustomDataRow;
procedure Clear;
procedure Parse(const Src : String; var Index : Integer);
end;
implementation
{ Support routines }
procedure SkipSpaces(const Src : String; var Index : Integer);
begin
while (Index <= Length(Src)) and
(CharInSet(Src[Index], [' ', #9, #10, #13])) do
Inc(Index);
end;
function GetNextDelimiter(const Src : String; var Index : Integer) : Char;
begin
SkipSpaces(Src, Index);
if Index > Length(Src) then
Result := #0
else begin
Result := Src[Index];
Inc(Index);
end;
end;
function GetNextToken(const Src : String; var Index : Integer) : String;
var
N : Integer;
begin
SkipSpaces(Src, Index);
N := Index;
while (Index <= Length(Src)) and
(CharInSet(Src[Index], ['a'..'z', 'A'..'Z'])) do
Inc(Index);
Result := Copy(Src, N, Index - N);
end;
function GetNextQuotedString(const Src : String; var Index : Integer) : String;
var
Delimiter : Char;
N : Integer;
begin
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '"' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '"']);
N := Index;
// Find closing double quote
while (Index <= Length(Src)) and (Src[Index] <> '"') do
Inc(Index);
Result := Copy(Src, N, Index - N);
if (Index <= Length(Src)) and (Src[Index] = '"') then
Inc(Index); // Skip closing double quote
end;
{ TCustomData }
procedure TCustomData.Clear;
begin
Name := '';
SetLength(Rows, 0);
end;
procedure TCustomData.Parse(const Src: String; var Index : Integer);
var
Token : String;
Delimiter : Char;
NRow : Integer;
begin
Clear;
while Index <= Length(Src) do begin
Token := GetNextToken(Src, Index);
if Token = '' then
break;
Name := Token;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '=' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '=']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '{' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '{']);
while TRUE do begin
// Add a new row
NRow := Length(Rows);
SetLength(Rows, NRow + 1);
Rows[NRow].Parse(Src, Index);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '}' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '{']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ',' then begin
Dec(Index);
break;
end;
end;
end;
end;
{ TCustomDataRow }
procedure TCustomDataRow.Clear;
begin
Name := '';
SetLength(Items, 0);
end;
procedure TCustomDataRow.Parse(const Src: String; var Index : Integer);
var
Delimiter : Char;
QuotedString : String;
NRow : Integer;
begin
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '[' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '[']);
QuotedString := GetNextQuotedString(Src, Index);
if QuotedString = '' then
raise Exception.CreateFmt('Missing quoted string at index %d', [Index]);
Name := QuotedString;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ']' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, ']']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '=' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '=']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '{' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '{']);
while TRUE do begin
NRow := Length(Items);
SetLength(Items, NRow + 1);
Items[NRow].Parse(Src, Index);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ',' then begin
Dec(Index); // Go back to the delimiter
break;
end;
// Last item is allowed to have an extra coma
Delimiter := GetNextDelimiter(Src, Index);
Dec(Index); // Go back to the delimiter
if Delimiter <> '[' then
break;
end;
end;
{ TCustomDataItem }
procedure TCustomDataItem.Clear;
begin
Name := '';
ShortName := '';
Description := '';
end;
procedure TCustomDataItem.Parse(const Src: String; var Index: Integer);
var
Delimiter : Char;
QuotedString : String;
begin
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '[' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '[']);
QuotedString := GetNextQuotedString(Src, Index);
if QuotedString = '' then
raise Exception.CreateFmt('Missing quoted string at index %d', [Index]);
Name := QuotedString;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ']' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, ']']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '=' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '=']);
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '{' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '{']);
QuotedString := GetNextQuotedString(Src, Index);
if QuotedString = '' then
raise Exception.CreateFmt('Missing quoted string at index %d', [Index]);
ShortName := QuotedString;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> ',' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, ',']);
QuotedString := GetNextQuotedString(Src, Index);
if QuotedString = '' then
raise Exception.CreateFmt('Missing quoted string at index %d', [Index]);
Description := QuotedString;
Delimiter := GetNextDelimiter(Src, Index);
if Delimiter <> '}' then
raise Exception.CreateFmt(UnexpectedDelimiter,
[Index, Delimiter, '}']);
end;
end.