我有一个管道分隔文件,我想拆分(我正在使用C#)。例如:
This|is|a|test
但是,某些数据可能包含管道。如果是,它将使用反斜杠进行转义:
This|is|a|pip\|ed|test (this is a pip|ed test)
我想知道是否有一个正则表达式或其他方法可以将它拆分为仅仅“纯”管道(即在它们前面没有反斜杠的管道)。我当前的方法是使用自定义位文本替换转义的管道,拆分管道,然后用管道替换我的自定义文本。不是很优雅,我不禁想到有更好的方法。谢谢你的帮助。
答案 0 :(得分:7)
只需使用String.IndexOf()
查找下一个管道即可。如果前一个字符不是反斜杠,则使用String.Substring()
提取单词。或者,您可以使用String.IndexOfAny()
查找管道或反斜杠的下一个匹配项。
我做了很多像这样的解析,这真的很直接。采取我的方法,如果正确完成也会更快地运行。
修改强>
事实上,也许是这样的事情。看看这与RegEx解决方案的性能比较,将会很有趣。
public List<string> ParseWords(string s)
{
List<string> words = new List<string>();
int pos = 0;
while (pos < s.Length)
{
// Get word start
int start = pos;
// Get word end
pos = s.IndexOf('|', pos);
while (pos > 0 && s[pos - 1] == '\\')
{
pos++;
pos = s.IndexOf('|', pos);
}
// Adjust for pipe not found
if (pos < 0)
pos = s.Length;
// Extract this word
words.Add(s.Substring(start, pos - start));
// Skip over pipe
if (pos < s.Length)
pos++;
}
return words;
}
答案 1 :(得分:3)
这可以做到:
string test = @"This|is|a|pip\|ed|test (this is a pip|ed test)";
string[] parts = Regex.Split(test, @"(?<!(?<!\\)*\\)\|");
正则表达式基本上表示:拆分不带转义字符的管道。我不应该因此而受到任何赞扬,我只是劫持了正则表达式from this post并简化了它。
修改强>
就性能而言,与此线程中提供的手动解析方法相比,我发现使用OP提供的较长测试字符串,此Regex实现比Jonathon Wood的实现慢3到5倍。
话虽如此,如果你没有实例化或将单词添加到List<string>
并返回void,则Jon的方法比Regex.Split()
方法快5倍(0.01ms vs 。0.002ms)纯粹分裂弦。如果你添加管理和返回List<string>
的开销,它的速度大约是3.6倍(0.01ms对0.00275ms),平均在几百万次迭代中。我没有使用静态Regex.Split()进行此测试,而是创建了一个新的Regex实例,上面的表达式在我的测试循环之外,然后调用它的Split方法。
<强>更新强>
使用静态Regex.Split()函数实际上比重用表达式的实例快得多。通过这种实现,正则表达式的使用仅比Jon的实现慢约1.6倍(0.0043ms对0.00275ms)
使用我链接到的帖子的扩展正则表达式结果相同。
答案 2 :(得分:2)
我遇到了类似的情况,对我来说,管道数量是固定的(不是带有“\ |”的管道)。这就是我的处理方式。
string sPipeSplit = "This|is|a|pip\\|ed|test (this is a pip|ed test)";
string sTempString = sPipeSplit.Replace("\\|", "¬"); //replace \| with non printable character
string[] sSplitString = sTempString.Split('|');
//string sFirstString = sSplitString[0].Replace("¬", "\\|"); //If you have fixed number of fields and you are copying to other field use replace while copying to other field.
/* Or you could use a loop to replace everything at once
foreach (string si in sSplitString)
{
si.Replace("¬", "\\|");
}
*/
答案 3 :(得分:1)
这是另一种解决方案。
关于编程最美妙的事情之一是为同一问题提供解决方案的几种方法:
string text = @"This|is|a|pip\|ed|test"; //The original text
string parsed = ""; //Where you will store the parsed string
bool flag = false;
foreach (var x in text.Split('|')) {
bool endsWithArroba = x.EndsWith(@"\");
parsed += flag ? "|" + x + " " : endsWithArroba ? x.Substring(0, x.Length-1) : x + " ";
flag = endsWithArroba;
}
答案 4 :(得分:0)
Cory的解决方案非常好。但是,我不想与Regex合作,那么你可以简单地做一些搜索“\ |”的事情。并用其他字符替换它,然后进行拆分,然后再用“\ |”替换它。
另一种选择是进行拆分,然后检查所有字符串,如果最后一个字符是\,则将其与下一个字符串连接。
当然,所有这些都忽略了如果你需要在管道之前使用转义反斜杠而发生的事情......比如“\\ |”。
总的来说,我倾向于使用正则表达式。
坦率地说,我更喜欢使用FileHelpers,因为即使这不是逗号,但它基本上是相同的。他们有关于why you shouldn't write this stuff yourself的精彩故事。
答案 5 :(得分:0)
您可以使用正则表达式执行此操作。一旦您决定使用反斜杠作为转义字符,您就有两个转义案例来解释:
\|
这两个都可以在同一个正则表达式中完成。转义的反斜杠始终是两个\
个字符。连续的,转义的反斜杠将始终是偶数\
个字符。如果在管道之前找到奇数编号的\
序列,则表示您有几个转义的反斜杠,后跟一个转义管道。所以你想要使用这样的东西:
/^(?:((?:[^|\\]|(?:\\{2})|\\\|)+)(?:\||$))*/
或许会让人感到困惑,但它应该有效。说明:
^ #The start of a line
(?:...
[^|\\] #A character other than | or \ OR
(?:\\{2})* #An even number of \ characters OR
\\\| #A literal \ followed by a literal |
...)+ #Repeat the preceding at least once
(?:$|\|) #Either a literal | or the end of a line