正则表达式 C# 是否可以在替换中使用变量?
Regex C# is it possible to use a variable in substitution?
我在文本中得到了一堆字符串,看起来像这样:
h1. this is the Header
h3. this one the header too
h111. and this
我得到了函数,它假设处理这个文本取决于它被称为迭代的内容
public void ProcessHeadersInText(string inputText, int atLevel = 1)
因此如果被调用,输出应该如下所示
ProcessHeadersInText(inputText, 2)
输出应该是:
<h3>this is the Header<h3>
<h5>this one the header too<h5>
<h9 and this <h9>
(最后一个看起来像这样,因为如果 h
字母后的值大于 9
它应该在输出中是 9
)
所以,我开始考虑使用正则表达式。
示例如下https://regex101.com/r/spb3Af/1/
(如您所见,我想出了这样的正则表达式 (^(h([\d]+)\.+?)(.+?)$)
并尝试对其使用替换 <h></h>
)
这几乎是我要找的东西,但我需要在标题级别的工作中添加一些逻辑。
是否可以在替换中添加任何带有变量的作品?
或者我需要寻找其他方式? (首先提取所有标题,考虑函数变量和 header 的值替换 em,并且仅在使用我编写的正则表达式之后?)
您可以像下面使用的那样使用正则表达式来解决您的问题。
Regex.Replace(s, @"^(h\d+)\.(.*)$", @"<><>", RegexOptions.Multiline)
让我向你解释一下我在做什么
// This will capture the header number which is followed
// by a '.' but ignore the . in the capture
(h\d+)\.
// This will capture the remaining of the string till the end
// of the line (see the multi-line regex option being used)
(.*)$
括号会把它捕获到变量中,可以作为第一次捕获的"$1"和第二次捕获的"$2"
试试这个:
private static string ProcessHeadersInText(string inputText, int atLevel = 1)
{
// Group 1 = value after 'h'
// Group 2 = Content of header without leading whitespace
string pattern = @"^h(\d+)\.\s*(.*?)\r?$";
return Regex.Replace(inputText, pattern, match => EvaluateHeaderMatch(match, atLevel), RegexOptions.Multiline);
}
private static string EvaluateHeaderMatch(Match m, int atLevel)
{
int hVal = int.Parse(m.Groups[1].Value) + atLevel;
if (hVal > 9) { hVal = 9; }
return $"<h{hVal}>{m.Groups[2].Value}</h{hVal}>";
}
然后打电话
ProcessHeadersInText(input, 2);
这使用带有自定义计算器函数的 Regex.Replace(string, string, MatchEvaluator, RegexOptions) 重载。
您当然可以将此解决方案简化为具有内联 lambda 表达式的单个函数:
public static string ProcessHeadersInText(string inputText, int atLevel = 1)
{
string pattern = @"^h(\d+)\.\s*(.*?)\r?$";
return Regex.Replace(inputText, pattern,
match =>
{
int hVal = int.Parse(match.Groups[1].Value) + atLevel;
if (hVal > 9) { hVal = 9; }
return $"<h{hVal}>{match.Groups[2].Value}</h{hVal}>";
},
RegexOptions.Multiline);
}
您可以使用的正则表达式是
^h(\d+)\.+\s*(.+)
如果您需要确保匹配不跨行,您可以将\s
替换为[^\S\r\n]
。见 regex demo.
在 C# 中替换时,将第 1 组值解析为 int 并在 Regex.Replace
方法内的匹配求值器中递增该值。
下面是对您有帮助的示例代码:
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.IO;
public class Test
{
// Demo: https://regex101.com/r/M9iGUO/2
public static readonly Regex reg = new Regex(@"^h(\d+)\.+\s*(.+)", RegexOptions.Compiled | RegexOptions.Multiline);
public static void Main()
{
var inputText = "h1. Topic 1\r\nblah blah blah, because of bla bla bla\r\nh2. PartA\r\nblah blah blah\r\nh3. Part a\r\nblah blah blah\r\nh2. Part B\r\nblah blah blah\r\nh1. Topic 2\r\nand its cuz blah blah\r\nFIN";
var res = ProcessHeadersInText(inputText, 2);
Console.WriteLine(res);
}
public static string ProcessHeadersInText(string inputText, int atLevel = 1)
{
return reg.Replace(inputText, m =>
string.Format("<h{0}>{1}</h{0}>", (int.Parse(m.Groups[1].Value) > 9 ?
9 : int.Parse(m.Groups[1].Value) + atLevel), m.Groups[2].Value.Trim()));
}
}
请注意,我在 m.Groups[2].Value
上使用 .Trim()
,因为 .
匹配 \r
。您可以使用 TrimEnd('\r')
来删除这个字符。
此线程中有很多好的解决方案,但我认为您并不真的需要 Regex 解决方案来解决您的问题。为了好玩和挑战,这里有一个非正则表达式的解决方案:
using System;
using System.Linq;
public class Program
{
public static void Main()
{
string extractTitle(string x) => x.Substring(x.IndexOf(". ") + 2);
string extractNumber(string x) => x.Remove(x.IndexOf(". ")).Substring(1);
string build(string n, string t) => $"<h{n}>{t}</h{n}>";
var inputs = new [] {
"h1. this is the Header",
"h3. this one the header too",
"h111. and this" };
foreach (var line in inputs.Select(x => build(extractNumber(x), extractTitle(x))))
{
Console.WriteLine(line);
}
}
}
我使用 C#7 嵌套函数和 C#6 内插字符串。如果你愿意,我可以使用更多旧版 C#。代码应该易于阅读,如果需要我可以添加注释。
C#5 版本
using System;
using System.Linq;
public class Program
{
static string extractTitle(string x)
{
return x.Substring(x.IndexOf(". ") + 2);
}
static string extractNumber(string x)
{
return x.Remove(x.IndexOf(". ")).Substring(1);
}
static string build(string n, string t)
{
return string.Format("<h{0}>{1}</h{0}>", n, t);
}
public static void Main()
{
var inputs = new []{
"h1. this is the Header",
"h3. this one the header too",
"h111. and this"
};
foreach (var line in inputs.Select(x => build(extractNumber(x), extractTitle(x))))
{
Console.WriteLine(line);
}
}
}
我在文本中得到了一堆字符串,看起来像这样:
h1. this is the Header
h3. this one the header too
h111. and this
我得到了函数,它假设处理这个文本取决于它被称为迭代的内容
public void ProcessHeadersInText(string inputText, int atLevel = 1)
因此如果被调用,输出应该如下所示
ProcessHeadersInText(inputText, 2)
输出应该是:
<h3>this is the Header<h3>
<h5>this one the header too<h5>
<h9 and this <h9>
(最后一个看起来像这样,因为如果 h
字母后的值大于 9
它应该在输出中是 9
)
所以,我开始考虑使用正则表达式。
示例如下https://regex101.com/r/spb3Af/1/
(如您所见,我想出了这样的正则表达式 (^(h([\d]+)\.+?)(.+?)$)
并尝试对其使用替换 <h></h>
)
这几乎是我要找的东西,但我需要在标题级别的工作中添加一些逻辑。
是否可以在替换中添加任何带有变量的作品?
或者我需要寻找其他方式? (首先提取所有标题,考虑函数变量和 header 的值替换 em,并且仅在使用我编写的正则表达式之后?)
您可以像下面使用的那样使用正则表达式来解决您的问题。
Regex.Replace(s, @"^(h\d+)\.(.*)$", @"<><>", RegexOptions.Multiline)
让我向你解释一下我在做什么
// This will capture the header number which is followed
// by a '.' but ignore the . in the capture
(h\d+)\.
// This will capture the remaining of the string till the end
// of the line (see the multi-line regex option being used)
(.*)$
括号会把它捕获到变量中,可以作为第一次捕获的"$1"和第二次捕获的"$2"
试试这个:
private static string ProcessHeadersInText(string inputText, int atLevel = 1)
{
// Group 1 = value after 'h'
// Group 2 = Content of header without leading whitespace
string pattern = @"^h(\d+)\.\s*(.*?)\r?$";
return Regex.Replace(inputText, pattern, match => EvaluateHeaderMatch(match, atLevel), RegexOptions.Multiline);
}
private static string EvaluateHeaderMatch(Match m, int atLevel)
{
int hVal = int.Parse(m.Groups[1].Value) + atLevel;
if (hVal > 9) { hVal = 9; }
return $"<h{hVal}>{m.Groups[2].Value}</h{hVal}>";
}
然后打电话
ProcessHeadersInText(input, 2);
这使用带有自定义计算器函数的 Regex.Replace(string, string, MatchEvaluator, RegexOptions) 重载。
您当然可以将此解决方案简化为具有内联 lambda 表达式的单个函数:
public static string ProcessHeadersInText(string inputText, int atLevel = 1)
{
string pattern = @"^h(\d+)\.\s*(.*?)\r?$";
return Regex.Replace(inputText, pattern,
match =>
{
int hVal = int.Parse(match.Groups[1].Value) + atLevel;
if (hVal > 9) { hVal = 9; }
return $"<h{hVal}>{match.Groups[2].Value}</h{hVal}>";
},
RegexOptions.Multiline);
}
您可以使用的正则表达式是
^h(\d+)\.+\s*(.+)
如果您需要确保匹配不跨行,您可以将\s
替换为[^\S\r\n]
。见 regex demo.
在 C# 中替换时,将第 1 组值解析为 int 并在 Regex.Replace
方法内的匹配求值器中递增该值。
下面是对您有帮助的示例代码:
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.IO;
public class Test
{
// Demo: https://regex101.com/r/M9iGUO/2
public static readonly Regex reg = new Regex(@"^h(\d+)\.+\s*(.+)", RegexOptions.Compiled | RegexOptions.Multiline);
public static void Main()
{
var inputText = "h1. Topic 1\r\nblah blah blah, because of bla bla bla\r\nh2. PartA\r\nblah blah blah\r\nh3. Part a\r\nblah blah blah\r\nh2. Part B\r\nblah blah blah\r\nh1. Topic 2\r\nand its cuz blah blah\r\nFIN";
var res = ProcessHeadersInText(inputText, 2);
Console.WriteLine(res);
}
public static string ProcessHeadersInText(string inputText, int atLevel = 1)
{
return reg.Replace(inputText, m =>
string.Format("<h{0}>{1}</h{0}>", (int.Parse(m.Groups[1].Value) > 9 ?
9 : int.Parse(m.Groups[1].Value) + atLevel), m.Groups[2].Value.Trim()));
}
}
请注意,我在 m.Groups[2].Value
上使用 .Trim()
,因为 .
匹配 \r
。您可以使用 TrimEnd('\r')
来删除这个字符。
此线程中有很多好的解决方案,但我认为您并不真的需要 Regex 解决方案来解决您的问题。为了好玩和挑战,这里有一个非正则表达式的解决方案:
using System;
using System.Linq;
public class Program
{
public static void Main()
{
string extractTitle(string x) => x.Substring(x.IndexOf(". ") + 2);
string extractNumber(string x) => x.Remove(x.IndexOf(". ")).Substring(1);
string build(string n, string t) => $"<h{n}>{t}</h{n}>";
var inputs = new [] {
"h1. this is the Header",
"h3. this one the header too",
"h111. and this" };
foreach (var line in inputs.Select(x => build(extractNumber(x), extractTitle(x))))
{
Console.WriteLine(line);
}
}
}
我使用 C#7 嵌套函数和 C#6 内插字符串。如果你愿意,我可以使用更多旧版 C#。代码应该易于阅读,如果需要我可以添加注释。
C#5 版本
using System;
using System.Linq;
public class Program
{
static string extractTitle(string x)
{
return x.Substring(x.IndexOf(". ") + 2);
}
static string extractNumber(string x)
{
return x.Remove(x.IndexOf(". ")).Substring(1);
}
static string build(string n, string t)
{
return string.Format("<h{0}>{1}</h{0}>", n, t);
}
public static void Main()
{
var inputs = new []{
"h1. this is the Header",
"h3. this one the header too",
"h111. and this"
};
foreach (var line in inputs.Select(x => build(extractNumber(x), extractTitle(x))))
{
Console.WriteLine(line);
}
}
}