From 4cab1f15b4ed38f735db2f00ffde3946139b88d1 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Mon, 17 Mar 2008 16:01:34 +0000 Subject: [PATCH] Added: TNamedFormatStringList Changed: NamedFormat now requires names to be enclosed in <> (mainly for readability, partially because it makes parsing more reliable) --- X2UtNamedFormat.pas | 62 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/X2UtNamedFormat.pas b/X2UtNamedFormat.pas index e966809..cb3d9cd 100644 --- a/X2UtNamedFormat.pas +++ b/X2UtNamedFormat.pas @@ -9,22 +9,39 @@ unit X2UtNamedFormat; 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 - 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: + + %:s %:.2d + AParams contains alternating the parameter name and it's value. Note: NamedFormat works by mapping names to indices and passing the result - to SysUtils.Format. Unnamed specifiers will therefore be affected by - named specifiers! It is recommended to name all specifiers. + to SysUtils.Format. Unnamed or existing indexed specifiers will therefore + be affected by named specifiers! It is strongly recommended to name all + specifiers. } function NamedFormat(const AFormat: String; AParams: array of const): String; implementation uses - Classes, SysUtils; @@ -33,8 +50,10 @@ type const - SpecifierChar = '%'; - ValidNameChars = ['A'..'Z', 'a'..'z', '0'..'9']; + SpecifierChar = '%'; + SpecifierNameStart = '<'; + SpecifierNameEnd = '>'; + ValidNameChars = ['A'..'Z', 'a'..'z', '0'..'9', '_']; procedure StreamWriteChar(const AStream: TStream; const AValue: Char); @@ -59,7 +78,7 @@ begin while position < AEnd do begin - if position^ = ':' then + if position^ = SpecifierNameEnd then begin Result := position; break; @@ -116,9 +135,11 @@ begin StreamWriteChar(formatStream, currentPos^); Inc(currentPos); - { Check if this is not an escape character } - if (currentPos < formatEnd) and (currentPos^ <> SpecifierChar) then + { Check if this is indeed a named specifier } + if (currentPos < formatEnd) and (currentPos^ = SpecifierNameStart) then begin + Inc(currentPos); + nameStart := currentPos; nameEnd := FindNameEnd(currentPos, formatEnd); @@ -131,12 +152,13 @@ begin specifierIndex := paramNames.Add(name); StreamWriteString(formatStream, IntToStr(specifierIndex)); + currentPos := nameEnd; end; end; - end; + end else + StreamWriteChar(formatStream, currentPos^); - StreamWriteChar(formatStream, currentPos^); Inc(currentPos); end; @@ -165,11 +187,9 @@ begin Inc(paramIndex); specifierIndex := paramNames.IndexOf(name); - if specifierIndex = -1 then - raise Exception.CreateFmt('Parameter "%s" could not be found in the format string', - [name]); + if specifierIndex > -1 then + paramValues[specifierIndex] := AParams[paramIndex]; - paramValues[specifierIndex] := AParams[paramIndex]; Inc(paramIndex); end; finally @@ -180,4 +200,16 @@ begin end; +{ TNamedFormatStringList } +procedure TNamedFormatStringList.AddLn; +begin + Add(''); +end; + + +function TNamedFormatStringList.Format(AParams: array of const): String; +begin + Result := NamedFormat(Text, AParams); +end; + end.