我有一个程序可以从存储在XML文件中的C#位生成C#。如果我有一个代码片段:
foo {bar}
我需要将其转换为插值字符串,如下所示:
$@"foo {bar}"
问题在于,如果我在占位符之外有引号,例如:
"foo" {bar}
我需要加倍:
$@"""foo"" {bar}"
但忽略占位符内的引号:
foo {"bar"}
应该产生:
$@"foo {"bar"}"
另外,需要注意双重括号:
foo {{"bar"}}
应该产生:
$@"foo {{""bar""}}"
也许最棘手的是,如果占位符前面和/或后面跟着偶数个括号:
foo {{{"bar"}}}
应该产生:
$@"foo {{{"bar"}}}"
简而言之,如果有占位符则忽略其中的所有内容。对于文本的其余部分,请加双引号。
这可以使用正则表达式完成吗?如果没有,我有什么替代方案?
答案 0 :(得分:1)
您至少需要两个步骤:
在表达式中添加引号:
"(?=[^}]*(?:}})*[^}]*$)|(?<=^[^{]*(?:{{)*)"
=&gt;
替换为""
请参阅demo
$@"..."
string.Format("$@\"{0}\"", str);
醇>
var s = "\"foo\" {bar}";
var rx = new Regex(@"(?<!(?<!{){[^{}]*)""(?![^{}]*}(?!}))");
Console.WriteLine(string.Format("$@\"{0}\"",rx.Replace(s,"\"\"")));
答案 1 :(得分:1)
使用正则表达式无法做到这一点。知道占位符何时启动很容易,知道它何时结束是困难的部分,因为占位符几乎可以容纳任何C#表达式,所以你必须跟踪块({}
)和文字(字符串,字符,注释) )因为文字中的任何括号都不重要。
这是我提出的代码:
enum ParsingMode {
Text,
Code,
InterpolatedString,
InterpolatedVerbatimString,
String,
VerbatimString,
Char,
MultilineComment
}
public static string EscapeValueTemplate(string valueTemplate) {
if (valueTemplate == null) throw new ArgumentNullException(nameof(valueTemplate));
var quoteIndexes = new List<int>();
var modeStack = new Stack<ParsingMode>();
modeStack.Push(ParsingMode.Text);
Func<ParsingMode> currentMode = () => modeStack.Peek();
for (int i = 0; i < valueTemplate.Length; i++) {
char c = valueTemplate[i];
Func<char?> nextChar = () =>
i + 1 < valueTemplate.Length ? valueTemplate[i + 1]
: default(char?);
switch (currentMode()) {
case ParsingMode.Code:
switch (c) {
case '{':
modeStack.Push(ParsingMode.Code);
break;
case '}':
modeStack.Pop();
break;
case '\'':
modeStack.Push(ParsingMode.Char);
break;
case '"':
ParsingMode stringMode = ParsingMode.String;
switch (valueTemplate[i - 1]) {
case '@':
if (i - 2 >= 0 && valueTemplate[i - 2] == '$') {
stringMode = ParsingMode.InterpolatedVerbatimString;
} else {
stringMode = ParsingMode.VerbatimString;
}
break;
case '$':
stringMode = ParsingMode.InterpolatedString;
break;
}
modeStack.Push(stringMode);
break;
case '/':
if (nextChar() == '*') {
modeStack.Push(ParsingMode.MultilineComment);
i++;
}
break;
}
break;
case ParsingMode.Text:
case ParsingMode.InterpolatedString:
case ParsingMode.InterpolatedVerbatimString:
switch (c) {
case '{':
if (nextChar() == '{') {
i++;
} else {
modeStack.Push(ParsingMode.Code);
}
break;
case '"':
switch (currentMode()) {
case ParsingMode.Text:
quoteIndexes.Add(i);
break;
case ParsingMode.InterpolatedString:
modeStack.Pop();
break;
case ParsingMode.InterpolatedVerbatimString:
if (nextChar() == '"') {
i++;
} else {
modeStack.Pop();
}
break;
}
break;
case '\\':
if (currentMode() == ParsingMode.InterpolatedString) {
i++;
}
break;
}
break;
case ParsingMode.String:
switch (c) {
case '\\':
i++;
break;
case '"':
modeStack.Pop();
break;
}
break;
case ParsingMode.VerbatimString:
if (c == '"') {
if (nextChar() == '"') {
i++;
} else {
modeStack.Pop();
}
}
break;
case ParsingMode.Char:
switch (c) {
case '\\':
i++;
break;
case '\'':
modeStack.Pop();
break;
}
break;
case ParsingMode.MultilineComment:
if (c == '*') {
if (nextChar() == '/') {
modeStack.Pop();
i++;
}
}
break;
}
}
var sb = new StringBuilder(valueTemplate, valueTemplate.Length + quoteIndexes.Count);
for (int i = 0; i < quoteIndexes.Count; i++) {
sb.Insert(quoteIndexes[i] + i, '"');
}
return sb.ToString();
}