1
0
mirror of synced 2024-09-19 17:56:09 +00:00

Added: TNamedFormatStringList

Changed: NamedFormat now requires names to be enclosed in <> (mainly for readability, partially because it makes parsing more reliable)
This commit is contained in:
Mark van Renswoude 2008-03-17 16:01:34 +00:00
parent 263c324676
commit 4cab1f15b4

View File

@ -9,22 +9,39 @@
unit X2UtNamedFormat; unit X2UtNamedFormat;
interface interface
uses
Classes;
type
TNamedFormatStringList = class(TStringList)
public
procedure AddLn();
function Format(AParams: array of const): String;
end;
{ {
AFormat uses the same format strings as SysUtils.Format, where each AFormat uses the same format strings as SysUtils.Format, where each
format specifier may use a named instead of a numeric index. format specifier may use a named instead of a numeric index, surrounded by
<>, eg:
%<Value1>:s %<Value2>:.2d
AParams contains alternating the parameter name and it's value. AParams contains alternating the parameter name and it's value.
Note: NamedFormat works by mapping names to indices and passing the result Note: NamedFormat works by mapping names to indices and passing the result
to SysUtils.Format. Unnamed specifiers will therefore be affected by to SysUtils.Format. Unnamed or existing indexed specifiers will therefore
named specifiers! It is recommended to name all specifiers. be affected by named specifiers! It is strongly recommended to name all
specifiers.
} }
function NamedFormat(const AFormat: String; AParams: array of const): String; function NamedFormat(const AFormat: String; AParams: array of const): String;
implementation implementation
uses uses
Classes,
SysUtils; SysUtils;
@ -33,8 +50,10 @@ type
const const
SpecifierChar = '%'; SpecifierChar = '%';
ValidNameChars = ['A'..'Z', 'a'..'z', '0'..'9']; SpecifierNameStart = '<';
SpecifierNameEnd = '>';
ValidNameChars = ['A'..'Z', 'a'..'z', '0'..'9', '_'];
procedure StreamWriteChar(const AStream: TStream; const AValue: Char); procedure StreamWriteChar(const AStream: TStream; const AValue: Char);
@ -59,7 +78,7 @@ begin
while position < AEnd do while position < AEnd do
begin begin
if position^ = ':' then if position^ = SpecifierNameEnd then
begin begin
Result := position; Result := position;
break; break;
@ -116,9 +135,11 @@ begin
StreamWriteChar(formatStream, currentPos^); StreamWriteChar(formatStream, currentPos^);
Inc(currentPos); Inc(currentPos);
{ Check if this is not an escape character } { Check if this is indeed a named specifier }
if (currentPos < formatEnd) and (currentPos^ <> SpecifierChar) then if (currentPos < formatEnd) and (currentPos^ = SpecifierNameStart) then
begin begin
Inc(currentPos);
nameStart := currentPos; nameStart := currentPos;
nameEnd := FindNameEnd(currentPos, formatEnd); nameEnd := FindNameEnd(currentPos, formatEnd);
@ -131,12 +152,13 @@ begin
specifierIndex := paramNames.Add(name); specifierIndex := paramNames.Add(name);
StreamWriteString(formatStream, IntToStr(specifierIndex)); StreamWriteString(formatStream, IntToStr(specifierIndex));
currentPos := nameEnd; currentPos := nameEnd;
end; end;
end; end;
end; end else
StreamWriteChar(formatStream, currentPos^);
StreamWriteChar(formatStream, currentPos^);
Inc(currentPos); Inc(currentPos);
end; end;
@ -165,11 +187,9 @@ begin
Inc(paramIndex); Inc(paramIndex);
specifierIndex := paramNames.IndexOf(name); specifierIndex := paramNames.IndexOf(name);
if specifierIndex = -1 then if specifierIndex > -1 then
raise Exception.CreateFmt('Parameter "%s" could not be found in the format string', paramValues[specifierIndex] := AParams[paramIndex];
[name]);
paramValues[specifierIndex] := AParams[paramIndex];
Inc(paramIndex); Inc(paramIndex);
end; end;
finally finally
@ -180,4 +200,16 @@ begin
end; end;
{ TNamedFormatStringList }
procedure TNamedFormatStringList.AddLn;
begin
Add('');
end;
function TNamedFormatStringList.Format(AParams: array of const): String;
begin
Result := NamedFormat(Text, AParams);
end;
end. end.