C # 中的命名字符串格式

有没有什么方法可以通过名称而不是 C # 中的位置来格式化字符串?

在 python 中,我可以做类似于下面这个例子的事情(无耻地从 给你中盗用) :

>>> print '%(language)s has %(#)03d quote types.' % \
{'language': "Python", "#": 2}
Python has 002 quote types.

在 C # 中有什么方法可以做到这一点吗? 例如:

String.Format("{some_variable}: {some_other_variable}", ...);

能够使用变量名来实现这一点固然不错,但是使用字典也是可以接受的。

82568 次浏览

我认为最接近的是索引格式:

String.Format("{0} has {1} quote types.", "C#", "1");

还有 String。Replace () ,如果你愿意分几步来做,并且相信你不会在字符串的其他地方找到你的“变量”:

string MyString = "{language} has {n} quote types.";
MyString = MyString.Replace("{language}", "C#").Replace("{n}", "1");

将其扩展为使用 List:

List<KeyValuePair<string, string>> replacements = GetFormatDictionary();
foreach (KeyValuePair<string, string> item in replacements)
{
MyString = MyString.Replace(item.Key, item.Value);
}

也可以使用 Dictionary < string,string > 迭代它的。键集合,但是通过使用 List < KeyValuePair < string,string > > 我们可以利用 List 的。方法,并将其浓缩为一行程序:

replacements.ForEach(delegate(KeyValuePair<string,string>) item) { MyString = MyString.Replace(item.Key, item.Value);});

Lambda 会更简单,但我还是要去。净值2.0。还要注意。中的字符串以迭代方式使用时,Replace ()的性能并不理想。网络是不可改变的。此外,这要求 MyString变量的定义方式使得委托可以访问它,因此它还不完美。

我怀疑这是不可能的。我首先想到的是如何访问本地变量名?

然而,可能有一些聪明的方法可以使用 LINQ 和 Lambda 表达式来实现这一点。

string language = "Python";
int numquotes = 2;
string output = language + " has "+ numquotes + " language types.";

编辑: 我应该说的是,“不,我不相信你想做的是由 C # 支持的。这是你能得到的最接近的结果。”

没有处理此问题的内置方法。

这里有一个方法

string myString = "{foo} is {bar} and {yadi} is {yada}".Inject(o);

还有一个

Status.Text = "{UserName} last logged in at {LastLoginDate}".FormatWith(user);

第三个改进的方法部分基于上面两个 ,来自 Phil Haack

似乎没有办法打破常规。不过,实现您自己的 IFormatProvider看起来是可行的,它链接到 IDictionary以获取值。

var Stuff = new Dictionary<string, object> {
{ "language", "Python" },
{ "#", 2 }
};
var Formatter = new DictionaryFormatProvider();


// Interpret {0:x} where {0}=IDictionary and "x" is hash key
Console.WriteLine string.Format(Formatter, "{0:language} has {0:#} quote types", Stuff);

产出:

Python has 2 quote types

The caveat is that you can't mix FormatProviders, so the fancy text formatting can't be used at the same time.

框架本身并没有提供这样做的方法,但是您可以看看 Scott Hanselman 编写的 这篇文章。示例用法:

Person p = new Person();
string foo = p.ToString("{Money:C} {LastName}, {ScottName} {BirthDate}");
Assert.AreEqual("$3.43 Hanselman, {ScottName} 1/22/1974 12:00:00 AM", foo);

James Newton-King 的这个代码 是类似的,它使用子属性和索引,

string foo = "Top result for {Name} was {Results[0].Name}".FormatWith(student));

James 的代码依赖于 System. Web.UI.DataBinder来解析字符串,并且需要引用 System。一些人不喜欢在非 Web 应用程序中使用 Web。

编辑: 哦,如果您没有一个具有属性的对象,那么它们可以很好地处理匿名类型:

string name = ...;
DateTime date = ...;
string foo = "{Name} - {Birthday}".FormatWith(new { Name = name, Birthday = date });

我刚刚在我的博客上发布了一个实现: http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx

它解决了其他实现在大括号转义方面的一些问题。帖子有详细内容。它负责数据绑定。Eval 的东西太多了,但还是很快。

参见 https://stackoverflow.com/questions/271398?page=2#358259

使用 link-to 扩展,您可以编写以下代码:

var str = "{foo} {bar} {baz}".Format(foo=>"foo", bar=>2, baz=>new object());

你会得到 "foo 2 System.Object”。

你也可以像这样使用匿名类型:

    public string Format(string input, object p)
{
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(p))
input = input.Replace("{" + prop.Name + "}", (prop.GetValue(p) ?? "(null)").ToString());


return input;
}

当然,如果您还想解析格式化,那么需要更多的代码,但是您可以使用以下函数格式化字符串:

Format("test {first} and {another}", new { first = "something", another = "something else" })

这是我前阵子做的。它使用带有单个参数的 Format 方法扩展 String。好处是它将使用标准字符串。格式,如果你提供一个简单的参数,如整型,但如果你使用匿名类型,它也将工作。

示例用法:

"The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" })

会导致“史密斯一家有4个孩子”

它不做数组和索引器之类的疯狂绑定工作,但是它超级简单,性能超级高。

    public static class AdvancedFormatString
{


/// <summary>
/// An advanced version of string.Format.  If you pass a primitive object (string, int, etc), it acts like the regular string.Format.  If you pass an anonmymous type, you can name the paramters by property name.
/// </summary>
/// <param name="formatString"></param>
/// <param name="arg"></param>
/// <returns></returns>
/// <example>
/// "The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" })
///
/// results in
/// "This Smith family has 4 children
/// </example>
public static string Format(this string formatString, object arg, IFormatProvider format = null)
{
if (arg == null)
return formatString;


var type = arg.GetType();
if (Type.GetTypeCode(type) != TypeCode.Object || type.IsPrimitive)
return string.Format(format, formatString, arg);


var properties = TypeDescriptor.GetProperties(arg);
return formatString.Format((property) =>
{
var value = properties[property].GetValue(arg);
return Convert.ToString(value, format);
});
}




public static string Format(this string formatString, Func<string, string> formatFragmentHandler)
{
if (string.IsNullOrEmpty(formatString))
return formatString;
Fragment[] fragments = GetParsedFragments(formatString);
if (fragments == null || fragments.Length == 0)
return formatString;


return string.Join(string.Empty, fragments.Select(fragment =>
{
if (fragment.Type == FragmentType.Literal)
return fragment.Value;
else
return formatFragmentHandler(fragment.Value);
}).ToArray());
}




private static Fragment[] GetParsedFragments(string formatString)
{
Fragment[] fragments;
if ( parsedStrings.TryGetValue(formatString, out fragments) )
{
return fragments;
}
lock (parsedStringsLock)
{
if ( !parsedStrings.TryGetValue(formatString, out fragments) )
{
fragments = Parse(formatString);
parsedStrings.Add(formatString, fragments);
}
}
return fragments;
}


private static Object parsedStringsLock = new Object();
private static Dictionary<string,Fragment[]> parsedStrings = new Dictionary<string,Fragment[]>(StringComparer.Ordinal);


const char OpeningDelimiter = '{';
const char ClosingDelimiter = '}';


/// <summary>
/// Parses the given format string into a list of fragments.
/// </summary>
/// <param name="format"></param>
/// <returns></returns>
static Fragment[] Parse(string format)
{
int lastCharIndex = format.Length - 1;
int currFragEndIndex;
Fragment currFrag = ParseFragment(format, 0, out currFragEndIndex);


if (currFragEndIndex == lastCharIndex)
{
return new Fragment[] { currFrag };
}


List<Fragment> fragments = new List<Fragment>();
while (true)
{
fragments.Add(currFrag);
if (currFragEndIndex == lastCharIndex)
{
break;
}
currFrag = ParseFragment(format, currFragEndIndex + 1, out currFragEndIndex);
}
return fragments.ToArray();


}


/// <summary>
/// Finds the next delimiter from the starting index.
/// </summary>
static Fragment ParseFragment(string format, int startIndex, out int fragmentEndIndex)
{
bool foundEscapedDelimiter = false;
FragmentType type = FragmentType.Literal;


int numChars = format.Length;
for (int i = startIndex; i < numChars; i++)
{
char currChar = format[i];
bool isOpenBrace = currChar == OpeningDelimiter;
bool isCloseBrace = isOpenBrace ? false : currChar == ClosingDelimiter;


if (!isOpenBrace && !isCloseBrace)
{
continue;
}
else if (i < (numChars - 1) && format[i + 1] == currChar)
{//\{\{ or }}
i++;
foundEscapedDelimiter = true;
}
else if (isOpenBrace)
{
if (i == startIndex)
{
type = FragmentType.FormatItem;
}
else
{


if (type == FragmentType.FormatItem)
throw new FormatException("Two consequtive unescaped { format item openers were found.  Either close the first or escape any literals with another {.");


//curr character is the opening of a new format item.  so we close this literal out
string literal = format.Substring(startIndex, i - startIndex);
if (foundEscapedDelimiter)
literal = ReplaceEscapes(literal);


fragmentEndIndex = i - 1;
return new Fragment(FragmentType.Literal, literal);
}
}
else
{//close bracket
if (i == startIndex || type == FragmentType.Literal)
throw new FormatException("A } closing brace existed without an opening { brace.");


string formatItem = format.Substring(startIndex + 1, i - startIndex - 1);
if (foundEscapedDelimiter)
formatItem = ReplaceEscapes(formatItem);//a format item with a { or } in its name is crazy but it could be done
fragmentEndIndex = i;
return new Fragment(FragmentType.FormatItem, formatItem);
}
}


if (type == FragmentType.FormatItem)
throw new FormatException("A format item was opened with { but was never closed.");


fragmentEndIndex = numChars - 1;
string literalValue = format.Substring(startIndex);
if (foundEscapedDelimiter)
literalValue = ReplaceEscapes(literalValue);


return new Fragment(FragmentType.Literal, literalValue);


}


/// <summary>
/// Replaces escaped brackets, turning '\{\{' and '}}' into '{' and '}', respectively.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
static string ReplaceEscapes(string value)
{
return value.Replace("\{\{", "{").Replace("}}", "}");
}


private enum FragmentType
{
Literal,
FormatItem
}


private class Fragment
{


public Fragment(FragmentType type, string value)
{
Type = type;
Value = value;
}


public FragmentType Type
{
get;
private set;
}


/// <summary>
/// The literal value, or the name of the fragment, depending on fragment type.
/// </summary>
public string Value
{
get;
private set;
}




}


}
private static Regex s_NamedFormatRegex = new Regex(@"\{(?!\{)(?<key>[\w]+)(:(?<fmt>(\{\{|\}\}|[^\{\}])*)?)?\}", RegexOptions.Compiled);


public static StringBuilder AppendNamedFormat(this StringBuilder builder,IFormatProvider provider, string format, IDictionary<string, object> args)
{
if (builder == null) throw new ArgumentNullException("builder");
var str = s_NamedFormatRegex.Replace(format, (mt) => {
string key = mt.Groups["key"].Value;
string fmt = mt.Groups["fmt"].Value;
object value = null;
if (args.TryGetValue(key,out value)) {
return string.Format(provider, "{0:" + fmt + "}", value);
} else {
return mt.Value;
}
});
builder.Append(str);
return builder;
}


public static StringBuilder AppendNamedFormat(this StringBuilder builder, string format, IDictionary<string, object> args)
{
if (builder == null) throw new ArgumentNullException("builder");
return builder.AppendNamedFormat(null, format, args);
}

例如:

var builder = new StringBuilder();
builder.AppendNamedFormat(
@"你好,{Name},今天是{Date:yyyy/MM/dd}, 这是你第{LoginTimes}次登录,积分{Score:\{\{ 0.00 }}}",
new Dictionary<string, object>() {
{ "Name", "wayjet" },
{ "LoginTimes",18 },
{ "Score", 100.4 },
{ "Date",DateTime.Now }
});

产出: 你好,wayjet,今天是2011-05-04,这是你第18次登录,积分{100.40}

看看这个:

public static string StringFormat(string format, object source)
{
var matches = Regex.Matches(format, @"\{(.+?)\}");
List<string> keys = (from Match matche in matches select matche.Groups[1].Value).ToList();


return keys.Aggregate(
format,
(current, key) =>
{
int colonIndex = key.IndexOf(':');
return current.Replace(
"{" + key + "}",
colonIndex > 0
? DataBinder.Eval(source, key.Substring(0, colonIndex), "{0:" + key.Substring(colonIndex + 1) + "}")
: DataBinder.Eval(source, key).ToString());
});
}

样本:

string format = "{foo} is a {bar} is a {baz} is a {qux:#.#} is a really big {fizzle}";
var o = new { foo = 123, bar = true, baz = "this is a test", qux = 123.45, fizzle = DateTime.Now };
Console.WriteLine(StringFormat(format, o));

与其他解决方案相比,性能非常好。

对于任何对象,这里有一个简单的方法:

    using System.Text.RegularExpressions;
using System.ComponentModel;


public static string StringWithFormat(string format, object args)
{
Regex r = new Regex(@"\{([A-Za-z0-9_]+)\}");


MatchCollection m = r.Matches(format);


var properties = TypeDescriptor.GetProperties(args);


foreach (Match item in m)
{
try
{
string propertyName = item.Groups[1].Value;
format = format.Replace(item.Value, properties[propertyName].GetValue(args).ToString());
}
catch
{
throw new FormatException("The format string is not valid");
}
}


return format;
}

以下是使用方法:

 DateTime date = DateTime.Now;
string dateString = StringWithFormat("{Month}/{Day}/{Year}", date);

产出: 2012年2月27日

我的开放源码库 瑞格特支持命名格式(以及其他一些东西)。目前的目标是。NET 4.0 + ,可在 NuGet上使用。我还有一篇关于它的博客文章: 正则表达式: 帮助您减少您的(问题){2}

命名格式位支持:

  • 基本格式
  • 嵌套属性格式设置
  • 字典格式
  • 分隔符的转义
  • 标准/自定义/IFormatProvider 字符串格式设置

例如:

var order = new
{
Description = "Widget",
OrderDate = DateTime.Now,
Details = new
{
UnitPrice = 1500
}
};


string template = "We just shipped your order of '{Description}', placed on {OrderDate:d}. Your \{\{credit}} card will be billed {Details.UnitPrice:C}.";


string result = Template.Format(template, order);
// or use the extension: template.FormatTemplate(order);

结果:

我们刚刚发出您的“小部件”的订单,放在2/28/2014。您的信用卡将被收取 $1,500.00。

查看该项目的 GitHub 链接(上面)和 wiki 以获取其他示例。

插入的字符串 被添加到 C # 6.0和 Visual Basic 14中

两者都是通过 Visual Studio 2015中新的 Roslyn编译器引入的。

  • C # 6.0:


    return $"{someVariable} and also {someOtherVariable}"

  • VB 14:

    return $"{someVariable} and also {someOtherVariable}"

值得注意的特性(在 VisualStudio2015IDE 中) :

  • 支持语法着色 -突出显示字符串中包含的变量
  • 支持重构 ——当重命名时,字符串中包含的变量也会被重命名
  • 实际上不仅支持变量名,而且支持 表情——例如,不仅 {index}可以工作,而且 {(index + 1).ToString().Trim()}也可以工作

尽情享受吧! (在 VS 中单击“发送微笑”)

我实现了这个简单的类,它复制了 String 的功能。格式(使用类除外)。您可以使用字典或类型来定义字段。

Https://github.com/sergueifedorov/namedformatstring

C # 6.0正在将这个功能添加到语言规范中,所以 NamedFormatString是为了向后兼容。

我用与现有解决方案稍有不同的方法解决了这个问题。 它执行命名项替换的核心(而不是一些已经执行的反射位)。它极其迅速和简单..。 这是我的解决办法:

/// <summary>
/// Formats a string with named format items given a template dictionary of the items values to use.
/// </summary>
public class StringTemplateFormatter
{
private readonly IFormatProvider _formatProvider;


/// <summary>
/// Constructs the formatter with the specified <see cref="IFormatProvider"/>.
/// This is defaulted to <see cref="CultureInfo.CurrentCulture">CultureInfo.CurrentCulture</see> if none is provided.
/// </summary>
/// <param name="formatProvider"></param>
public StringTemplateFormatter(IFormatProvider formatProvider = null)
{
_formatProvider = formatProvider ?? CultureInfo.CurrentCulture;
}


/// <summary>
/// Formats a string with named format items given a template dictionary of the items values to use.
/// </summary>
/// <param name="text">The text template</param>
/// <param name="templateValues">The named values to use as replacements in the formatted string.</param>
/// <returns>The resultant text string with the template values replaced.</returns>
public string FormatTemplate(string text, Dictionary<string, object> templateValues)
{
var formattableString = text;
var values = new List<object>();
foreach (KeyValuePair<string, object> value in templateValues)
{
var index = values.Count;
formattableString = ReplaceFormattableItem(formattableString, value.Key, index);
values.Add(value.Value);
}
return String.Format(_formatProvider, formattableString, values.ToArray());
}


/// <summary>
/// Convert named string template item to numbered string template item that can be accepted by <see cref="string.Format(string,object[])">String.Format</see>
/// </summary>
/// <param name="formattableString">The string containing the named format item</param>
/// <param name="itemName">The name of the format item</param>
/// <param name="index">The index to use for the item value</param>
/// <returns>The formattable string with the named item substituted with the numbered format item.</returns>
private static string ReplaceFormattableItem(string formattableString, string itemName, int index)
{
return formattableString
.Replace("{" + itemName + "}", "{" + index + "}")
.Replace("{" + itemName + ",", "{" + index + ",")
.Replace("{" + itemName + ":", "{" + index + ":");
}
}

它的使用方式如下:

    [Test]
public void FormatTemplate_GivenANamedGuid_FormattedWithB_ShouldFormatCorrectly()
{
// Arrange
var template = "My guid {MyGuid:B} is awesome!";
var templateValues = new Dictionary<string, object> { { "MyGuid", new Guid("{A4D2A7F1-421C-4A1D-9CB2-9C2E70B05E19}") } };
var sut = new StringTemplateFormatter();
// Act
var result = sut.FormatTemplate(template, templateValues);
//Assert
Assert.That(result, Is.EqualTo("My guid {a4d2a7f1-421c-4a1d-9cb2-9c2e70b05e19} is awesome!"));
}

希望有人觉得这有用!

尽管公认的答案给出了一些很好的例子,但是。注入和一些 Haack 示例不处理转义。许多还严重依赖于正则表达式(更慢) ,或 DataBinder。上不可用的。NET 核心,以及其他一些环境中。

考虑到这一点,我编写了一个简单的基于状态机的解析器,该解析器通过字符流,逐字符地写入 StringBuilder输出。它被实现为 String扩展方法,并且可以接受带有参数的 Dictionary<string, object>object作为输入(使用反射)。

它处理无限级别的 \{\{{escaping}}},并在输入包含不平衡大括号和/或其他错误时抛出 FormatException

public static class StringExtension {
/// <summary>
/// Extension method that replaces keys in a string with the values of matching object properties.
/// </summary>
/// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
/// <param name="injectionObject">The object whose properties should be injected in the string</param>
/// <returns>A version of the formatString string with keys replaced by (formatted) key values.</returns>
public static string FormatWith(this string formatString, object injectionObject) {
return formatString.FormatWith(GetPropertiesDictionary(injectionObject));
}


/// <summary>
/// Extension method that replaces keys in a string with the values of matching dictionary entries.
/// </summary>
/// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
/// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
/// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
public static string FormatWith(this string formatString, IDictionary<string, object> dictionary) {
char openBraceChar = '{';
char closeBraceChar = '}';


return FormatWith(formatString, dictionary, openBraceChar, closeBraceChar);
}
/// <summary>
/// Extension method that replaces keys in a string with the values of matching dictionary entries.
/// </summary>
/// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
/// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
/// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
public static string FormatWith(this string formatString, IDictionary<string, object> dictionary, char openBraceChar, char closeBraceChar) {
string result = formatString;
if (dictionary == null || formatString == null)
return result;


// start the state machine!


// ballpark output string as two times the length of the input string for performance (avoids reallocating the buffer as often).
StringBuilder outputString = new StringBuilder(formatString.Length * 2);
StringBuilder currentKey = new StringBuilder();


bool insideBraces = false;


int index = 0;
while (index < formatString.Length) {
if (!insideBraces) {
// currently not inside a pair of braces in the format string
if (formatString[index] == openBraceChar) {
// check if the brace is escaped
if (index < formatString.Length - 1 && formatString[index + 1] == openBraceChar) {
// add a brace to the output string
outputString.Append(openBraceChar);
// skip over braces
index += 2;
continue;
}
else {
// not an escaped brace, set state to inside brace
insideBraces = true;
index++;
continue;
}
}
else if (formatString[index] == closeBraceChar) {
// handle case where closing brace is encountered outside braces
if (index < formatString.Length - 1 && formatString[index + 1] == closeBraceChar) {
// this is an escaped closing brace, this is okay
// add a closing brace to the output string
outputString.Append(closeBraceChar);
// skip over braces
index += 2;
continue;
}
else {
// this is an unescaped closing brace outside of braces.
// throw a format exception
throw new FormatException($"Unmatched closing brace at position {index}");
}
}
else {
// the character has no special meaning, add it to the output string
outputString.Append(formatString[index]);
// move onto next character
index++;
continue;
}
}
else {
// currently inside a pair of braces in the format string
// found an opening brace
if (formatString[index] == openBraceChar) {
// check if the brace is escaped
if (index < formatString.Length - 1 && formatString[index + 1] == openBraceChar) {
// there are escaped braces within the key
// this is illegal, throw a format exception
throw new FormatException($"Illegal escaped opening braces within a parameter - index: {index}");
}
else {
// not an escaped brace, we have an unexpected opening brace within a pair of braces
throw new FormatException($"Unexpected opening brace inside a parameter - index: {index}");
}
}
else if (formatString[index] == closeBraceChar) {
// handle case where closing brace is encountered inside braces
// don't attempt to check for escaped braces here - always assume the first brace closes the braces
// since we cannot have escaped braces within parameters.


// set the state to be outside of any braces
insideBraces = false;


// jump over brace
index++;


// at this stage, a key is stored in current key that represents the text between the two braces
// do a lookup on this key
string key = currentKey.ToString();
// clear the stringbuilder for the key
currentKey.Clear();


object outObject;


if (!dictionary.TryGetValue(key, out outObject)) {
// the key was not found as a possible replacement, throw exception
throw new FormatException($"The parameter \"{key}\" was not present in the lookup dictionary");
}


// we now have the replacement value, add the value to the output string
outputString.Append(outObject);


// jump to next state
continue;
} // if }
else {
// character has no special meaning, add it to the current key
currentKey.Append(formatString[index]);
// move onto next character
index++;
continue;
} // else
} // if inside brace
} // while


// after the loop, if all braces were balanced, we should be outside all braces
// if we're not, the input string was misformatted.
if (insideBraces) {
throw new FormatException("The format string ended before the parameter was closed.");
}


return outputString.ToString();
}


/// <summary>
/// Creates a Dictionary from an objects properties, with the Key being the property's
/// name and the Value being the properties value (of type object)
/// </summary>
/// <param name="properties">An object who's properties will be used</param>
/// <returns>A <see cref="Dictionary"/> of property values </returns>
private static Dictionary<string, object> GetPropertiesDictionary(object properties) {
Dictionary<string, object> values = null;
if (properties != null) {
values = new Dictionary<string, object>();
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(properties);
foreach (PropertyDescriptor prop in props) {
values.Add(prop.Name, prop.GetValue(properties));
}
}
return values;
}
}

最终,所有的逻辑都归结为10个主要状态——当状态机位于括号之外,同样位于括号之内时,下一个字符要么是一个开括号,要么是一个转义的开括号,要么是一个闭括号,要么是一个转义的闭括号,要么是一个普通的字符。随着循环的进行,这些条件中的每一个都被单独处理,向输出 StringBuffer或键 StringBuffer添加字符。当参数关闭时,键 StringBuffer的值用于在字典中查找参数的值,然后将该值推送到输出 StringBuffer中。最后,返回输出 StringBuffer的值。