假设我想询问用户他们想要某个输出的格式,输出将包括填充字段。所以他们提供了类似这样的字符串:
"Output text including some field {FieldName1Value} and another {FieldName2Value} and so on..."
{}绑定的任何内容都应该是表中的列名,它们将用我正在编写的代码替换为存储的值。看起来很简单,我可以在任何匹配模式“{”+ FieldName +“}”的实例上执行string.Replace。但是,如果我还想让用户选择使用转义,那么他们可以像任何其他字符串一样使用括号。我在想他们提供“{{”或“}}”来逃避这个支架 - 对他们来说很容易。所以,他们可以提供类似的东西:
"Output text including some field {FieldName1Value} and another {FieldName2Value} but not this {{FieldName2Value}}"
但现在“{{FieldName2Value}}”将被视为任何其他字符串,并被Replace忽略。此外,如果他们决定将“{{{FieldName2Value}}}”这样的内容放在三个括号中,那么代码会将其解释为用括号括起来的字段值等等。
这是我被卡住的地方。我正在尝试使用RegEx,并想出了这个:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string format = (string)values[0];
ObservableCollection<CalloutFieldAliasMap> oc = (ObservableCollection<CalloutFieldAliasMap>)values[1];
foreach (CalloutFieldMap map in oc)
format = Regex.Replace(format, @"(?<!{){" + map.FieldName + "(?<!})}", " " + map.FieldAlias + " ", RegexOptions.IgnoreCase);
return format;
}
这适用于双括号{{}}的情况,但如果有三个,即{{{}}},则无效。三个括号在被视为{FieldValue}时被视为字符串。
感谢您的帮助。
答案 0 :(得分:3)
通过扩展正则表达式,可以适应文字的存在。
format = Regex.Replace(format,
@"(?<!([^{]|^){(?:{{)*){" + Regex.Escape(map.FieldName) + "}",
String.Format(" {0} ", map.FieldAlias),
RegexOptions.IgnoreCase | RegexOptions.Compiled);
表达式的第一部分(?<!([^{]|^){(?:{{)*){
指定{
前面必须有偶数{
个字符,以便标记字段标记的开头。因此,{FieldName}
和{{{FieldName}
将表示字段名称的开头,而{{FieldName}
和{{{{FieldName}
则不会。
结束}
只需要字段的结尾为简单}
。语法中存在一些含糊之处,因为{FieldName1Value}}}
可以被解析为带有FieldName1Value
的标记(后跟文字}
)或FieldName1Value}
。正则表达式假设前者。 (如果是后者,则可以用}(?!}(}})*)
替换它。
其他一些说明。我添加了Regex.Escape(map.FieldName)
,以便字段名称中的所有字符都被视为文字;并添加了RegexOptions.Compiled
标志。 (因为这是一个复杂的表达式并在循环中执行,所以它是编译的好选择。)
循环执行后,简单:
format = format.Replace("{{", "{").Replace("}}", "}")
可用于取消文字{{
和}}
字符。
答案 1 :(得分:1)
最简单的方法是使用String.Replace
将双括号替换为用户不能(或几乎肯定不会)输入的字符序列。然后替换你的字段,最后将替换转换回双括号。
例如,给定:
string replaceOpen = "{x"; // 'x' should be something like \u00ff, for example
string replaceClose = "x}";
string template = "Replace {ThisField} but not {{ThatField}}";
string temp = template.Replace("{{", replaceOpen).Replace("}}", replaceClose);
string converted = temp.Replace("{ThisField}", "Foo");
string final = converted.Replace(replaceOpen, "{{").Replace(replaceClose, "}});
它不是特别漂亮,但它很有效。
你如何去做将在很大程度上取决于你多久调用一次,以及你真正需要多快的速度。
答案 2 :(得分:1)
我有一个我写的扩展方法几乎可以满足您的要求,但是,虽然它确实使用双括号进行转义,但它不会像您建议的那样执行三重括号。这是方法(也在https://github.com/benallred/Icing/blob/master/Icing/Icing.Core/StringExtensions.cs的GitHub上):
private const string FormatTokenGroupName = "token";
private static readonly Regex FormatRegex = new Regex(@"(?<!\{)\{(?<" + FormatTokenGroupName + @">\w+)\}(?!\})", RegexOptions.Compiled);
public static string Format(this string source, IDictionary<string, string> replacements)
{
if (string.IsNullOrWhiteSpace(source) || replacements == null)
{
return source;
}
string replaced = replacements.Aggregate(source,
(current, pair) =>
FormatRegex.Replace(current,
new MatchEvaluator(match =>
(match.Groups[FormatTokenGroupName].Value == pair.Key
? pair.Value : match.Value))));
return replaced.Replace("{{", "{").Replace("}}", "}");
}
用法:
"This is my {FieldName}".Format(new Dictionary<string, string>() { { "FieldName", "value" } });
如果你添加这个更容易:
public static string Format(this string source, object replacements)
{
if (string.IsNullOrWhiteSpace(source) || replacements == null)
{
return source;
}
IDictionary<string, string> replacementsDictionary = new Dictionary<string, string>();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(replacements))
{
string token = propertyDescriptor.Name;
object value = propertyDescriptor.GetValue(replacements);
replacementsDictionary.Add(token, (value != null ? value.ToString() : String.Empty));
}
return Format(source, replacementsDictionary);
}
用法:
"This is my {FieldName}".Format(new { FieldName = "value" });
此方法的单元测试位于https://github.com/benallred/Icing/blob/master/Icing/Icing.Tests/Core/TestOf_StringExtensions.cs
如果这不起作用,那么您的理想解决方案对三个以上的括号有什么作用?换句话说,如果{{{FieldName}}}变为{value},那么{{} {{FieldName}}}}成为?那么{{{{{FieldName}}}}}等等呢?虽然这些情况不太可能,但仍需要有目的地处理。
答案 3 :(得分:0)
RegEx不会执行您想要的操作,因为它只知道它的当前状态以及可用的转换。它没有记忆的概念。您尝试解析的语言不规则,因此您永远无法编写RegEx来处理一般情况。您需要i
个表达式,其中i
是匹配大括号的数量。
这背后有很多理论,如果你很好奇,我会在底部提供一些链接。但基本上你要解析的语言是无上下文的,并且为了实现一般解决方案,你需要建模一个下推自动机,它使用一个堆栈来确保一个开括号具有匹配的右括号(是的,这是为什么大多数语言都有匹配的大括号。)
每次遇到{
时,都会把它放在堆栈上。如果遇到}
,则从堆栈中弹出。清空堆栈时,您将知道已到达字段的末尾。当然,这是问题的一个主要简化,但如果你正在寻找一个通用的解决方案,它应该让你朝着正确的方向前进。
http://en.wikipedia.org/wiki/Regular_language