我有一个管道分隔文件,我想拆分(我正在使用C#)。例如:
这|是| A |测试
但是,某些数据可能包含管道。如果是,它将使用反斜杠进行转义:
这|是| a | pip \ | ed | test(这是一个点测试)
我想知道是否有一个正则表达式或其他方法将它拆分为仅仅“纯”管道(即在它们前面没有反斜杠的管道)。我当前的方法是使用自定义位文本替换转义的管道,拆分管道,然后用管道替换我的自定义文本。不是很优雅,我不禁想到有更好的方法。谢谢你的帮助。
我有一个管道分隔文件,我想拆分(我正在使用C#)。例如:
这|是| A |测试
但是,某些数据可能包含管道。如果是,它将使用反斜杠进行转义:
这|是| a | pip \ | ed | test(这是一个点测试)
我想知道是否有一个正则表达式或其他方法将它拆分为仅仅“纯”管道(即在它们前面没有反斜杠的管道)。我当前的方法是使用自定义位文本替换转义的管道,拆分管道,然后用管道替换我的自定义文本。不是很优雅,我不禁想到有更好的方法。谢谢你的帮助。
只是用 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;
}
这样做:
string test = @"This|is|a|pip\|ed|test (this is a pip|ed test)";
string[] parts = Regex.Split(test, @"(?<!(?<!\\)*\\)\|");
正则表达式基本上表示:拆分不带转义字符的管道。我不应该因此而受到任何赞扬,我只是劫持了正则表达式 从这篇文章 并简化它。
编辑
在性能方面,与该线程中提供的手动解析方法相比,我发现这个Regex实现比使用OP提供的较长测试字符串的Jonathon Wood实现慢3到5倍。
话虽如此,如果你没有实例化或添加单词 List<string>
而且返回void,而Jon的方法比它快5倍 Regex.Split()
纯粹分裂弦的方法(0.01ms对0.002ms)。如果你加回管理和返回的开销 List<string>
它大约快了3.6倍(0.01ms对0.00275ms),在几百万次迭代中取平均值。我没有使用静态Regex.Split()进行此测试,而是创建了一个新的Regex实例,上面的表达式在我的测试循环之外,然后调用它的Split方法。
UPDATE
使用静态Regex.Split()函数实际上比重用表达式的实例快得多。通过这种实现,正则表达式的使用仅比Jon的实现慢约1.6倍(0.0043ms对0.00275ms)
使用我链接到的帖子的扩展正则表达式的结果是相同的。
我遇到了类似的情况,对我来说,管道数量是固定的(不是带有“\ |”的管道)。这就是我的处理方式。
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("¬", "\\|");
}
*/
这是另一种解决方案。
关于编程最美妙的事情之一是为同一问题提供解决方案的几种方法:
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;
}
Cory的解决方案非常好。但是,我不想与Regex合作,那么你可以简单地做一些搜索“\ |”的事情。并用其他字符替换它,然后进行拆分,然后再用“\ |”替换它。
另一种选择是进行拆分,然后检查所有字符串,如果最后一个字符是\,则将其与下一个字符串连接。
当然,如果你需要在管道之前使用转义反斜杠,那么所有这些都会忽略会发生什么......比如“\\ |”。
总的来说,我倾向于正则表达式。
坦率地说,我更喜欢使用 FileHelpers 因为,尽管这不是逗号,但它基本上是相同的。他们有一个很棒的故事 为什么你不应该自己写这些东西。
你可以用正则表达式做到这一点。一旦您决定使用反斜杠作为转义字符,您有两个转义案例来解释:
\|
这两个都可以在同一个正则表达式中完成。转义的反斜杠总是两个 \
人物在一起。连续的,逃逸的反斜杠总是偶数 \
字符。如果你找到一个奇数序列的 \
在管道之前,这意味着你有几个转义的反斜杠,然后是一个转义的管道。所以你想要使用这样的东西:
/^(?:((?:[^|\\]|(?:\\{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