仅替换每个新索引前两行中的 string
支付老李
结论
可以用计数器处理。遍历 List<string> 时,同时记录当前数字索引和组内行号。索引变化后,将组内行号重置为 0。随后,在每组第 1、2 行中,把字段位置上的 description 分别替换为 Type 和 Process。
替换时应限定字段位置,并匹配完整单词 description,不要直接对整行做无条件字符串替换。
C# 实现
下面的代码假设相同数字索引的记录连续排列:
using System.Collections.Generic;
using System.Text.RegularExpressions;
var lines = new List<string>
{
"1 description Payment",
"1 description Order",
"1 ID 0222ed48-ddc1-3e19-4770-616c14935ca1",
"",
"2 description Refund",
"2 description Partial",
"2 description Contacted?",
"2 ID 87f11531-0493-4f5a-86d2-c8be0c23678f",
"",
"3 description Refund",
"3 description Partial",
"3 ID 87f11531-0493-4f5a-86d2-c8be0c23678f"
};
var indexPattern = new Regex(@"^\s*(?<index>\d+)\b");
var descriptionPattern = new Regex(
@"^(?<prefix>\s*\d+\s+)description\b",
RegexOptions.IgnoreCase
);
var result = new List<string>(lines.Count);
string? currentIndex = null;
int rowInGroup = 0;
foreach (string line in lines)
{
Match indexMatch = indexPattern.Match(line);
// 空行或不以数字索引开头的行原样保留
if (!indexMatch.Success)
{
result.Add(line);
continue;
}
string index = indexMatch.Groups["index"].Value;
if (index != currentIndex)
{
currentIndex = index;
rowInGroup = 0;
}
rowInGroup++;
string? replacement = rowInGroup switch
{
1 => "Type",
2 => "Process",
_ => null
};
if (replacement != null)
{
string updatedLine = descriptionPattern.Replace(
line,
match => match.Groups["prefix"].Value + replacement,
1
);
result.Add(updatedLine);
}
else
{
result.Add(line);
}
}
foreach (string line in result)
{
Console.WriteLine(line);
}
输出结果:
1 Type Payment
1 Process Order
1 ID 0222ed48-ddc1-3e19-4770-616c14935ca1
2 Type Refund
2 Process Partial
2 description Contacted?
2 ID 87f11531-0493-4f5a-86d2-c8be0c23678f
3 Type Refund
3 Process Partial
3 ID 87f11531-0493-4f5a-86d2-c8be0c23678f
为什么要记录索引和组内行号
如果只在计数器超过 2 后重置,一旦各索引下的行数不同,计数就会错位。计数器应该在数字索引变化时重置:
if (index != currentIndex)
{
currentIndex = index;
rowInGroup = 0;
}
这样,无论一个索引下有 2 行、3 行还是更多行,遇到新索引后都会重新计数,前两行也能得到正确处理。
如果要处理前两个 description
前面的代码处理的是每个索引下的前两行。如果实际需求是替换每个索引下最先出现的两个 description,即使中间夹着 ID 等其他行也要继续查找,那么计数器应只在匹配到 description 时递增:
string? currentIndex = null;
int descriptionCount = 0;
foreach (string line in lines)
{
Match indexMatch = indexPattern.Match(line);
if (!indexMatch.Success)
{
result.Add(line);
continue;
}
string index = indexMatch.Groups["index"].Value;
if (index != currentIndex)
{
currentIndex = index;
descriptionCount = 0;
}
Match descriptionMatch = descriptionPattern.Match(line);
if (!descriptionMatch.Success)
{
result.Add(line);
continue;
}
descriptionCount++;
string replacement = descriptionCount switch
{
1 => "Type",
2 => "Process",
_ => "description"
};
string updatedLine = descriptionPattern.Replace(
line,
match => match.Groups["prefix"].Value + replacement,
1
);
result.Add(updatedLine);
}
注意事项
- 按索引变化重置计数器的写法要求相同索引的行连续出现。如果索引
1的记录可能稍后再次出现,应改用Dictionary<string, int>,分别保存每个索引的计数。 - 正则表达式会保留数字索引后的原始空白,不会破坏现有的对齐格式。
- 如果示例中的
**Type**和**Process**只是 Markdown 加粗标记,实际字符串应写成Type和Process。如果星号本身也是数据的一部分,则把替换值改为"**Type**"和"**Process**"。 - 如果字段名区分大小写,可以移除
RegexOptions.IgnoreCase。
备注:内容仅供参考。
