插入字符串的转义引号
本文关键字:转义 字符串 插入 | 更新日期: 2023-09-27 18:04:53
我有一个程序,它从存储在XML文件中的c#位生成c#。如果我有这样的代码片段:
foo {bar}
我需要把它转换成一个插值字符串,像这样:
$@"foo {bar}"
问题是,如果我在占位符外加上引号,例如:
"foo" {bar}
我需要把它们翻倍:
$@"""foo"" {bar}"
但忽略占位符内的引号:
foo {"bar"}
应该产生:
$@"foo {"bar"}"
还有,需要注意双括号:
foo {{"bar"}}
应该产生:
$@"foo {{""bar""}}"
可能是最棘手的,如果占位符前面和/或后面有偶数个大括号:
foo {{{"bar"}}}
应该产生:
$@"foo {{{"bar"}}}"
简而言之,如果有占位符,则忽略其中的所有内容。其余部分用双引号可以使用正则表达式完成吗?如果不是,我有什么选择?
你至少需要两个步骤:
-
在表达式中添加引号:
"(?=[^}]*(?:}})*[^}]*$)|(?<=^[^{]*(?:{{)*)"
=>替换为""
看到演示
- 将
$@"..."
与string.Format("$@'"{0}'"", str);
围在一起
这是一个IDEONE演示
var s = "'"foo'" {bar}";
var rx = new Regex(@"(?<!(?<!{){[^{}]*)""(?![^{}]*}(?!}))");
Console.WriteLine(string.Format("$@'"{0}'"",rx.Replace(s,"'"'"")));
这里还有一个演示
正则表达式不能这样做。知道占位符何时开始是容易的,知道它何时结束是困难的部分,因为占位符几乎可以容纳任何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();
}