问题 C# - 在数据中带有转义管道的管道上拆分?


我有一个管道分隔文件,我想拆分(我正在使用C#)。例如:

这|是| A |测试

但是,某些数据可能包含管道。如果是,它将使用反斜杠进行转义:

这|是| a | pip \ | ed | test(这是一个点测试)

我想知道是否有一个正则表达式或其他方法将它拆分为仅仅“纯”管道(即在它们前面没有反斜杠的管道)。我当前的方法是使用自定义位文本替换转义的管道,拆分管道,然后用管道替换我的自定义文本。不是很优雅,我不禁想到有更好的方法。谢谢你的帮助。


9300
2018-04-28 04:22


起源

你见过吗? 这个(怪物)线程。不是一个直接的答案,但希望推动正确的方向。 - dawebber
如果你想在其中一个片段的末尾加上字面反斜杠怎么办? - Random832


答案:


只是用 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;
}

6
2018-04-28 04:29



是的,这更好,解析 string 在你自己的方式而不是使用 regex。这运行得更快。 +1 - KaeL
如果你没有添加单词 List<string> 并且返回它,手动解析方法比正则表达式方法快约5倍。如果你加回管理a的开销 List<string>,无论如何,它在我的机器上快了大约3倍。 - Cᴏʀʏ
@Cory:很有意思。谢谢! - Jonathan Wood
看到我的更新......我改变了我的测试并将Regex实现降低了大约1.6倍,但是,你仍然赢了! - Cᴏʀʏ


这样做:

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)

使用我链接到的帖子的扩展正则表达式的结果是相同的。


3
2018-04-28 04:38



假设反斜杠也可以被转义(例如 "This|is|a|pip\\|ed|test (this is a pip|ed test)"),这不起作用。您需要使用上述帖子中的完整内容。 - porges
@你是对的Porges。当我决定写一些关于它的代码时,这是我想到的第一件事:) - Oscar Mederos


我遇到了类似的情况,对我来说,管道数量是固定的(不是带有“\ |”的管道)。这就是我的处理方式。

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("¬", "\\|");
}
*/

2
2017-12-15 06:21





这是另一种解决方案。

关于编程最美妙的事情之一是为同一问题提供解决方案的几种方法:

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;
}

1
2018-04-28 04:51



这很漂亮,但如果你担心性能,这不是一个很好的选择。 - Jonathan Wood
@Jonathan正如我所说,这只是另一种方式。粘贴与您提供的代码类似的代码是没有意义的。我同意你的观点,虽然表现可能不是我在这个问题上非常重要的事情。 - Oscar Mederos
我没有批评你发布它。事实上,我提到它很光滑。我只是在评论这种方法的表现。 - Jonathan Wood
我认为这个和@ Jonathan都很漂亮,所以两者都是+1。 - Justin Morgan


Cory的解决方案非常好。但是,我不想与Regex合作,那么你可以简单地做一些搜索“\ |”的事情。并用其他字符替换它,然后进行拆分,然后再用“\ |”替换它。

另一种选择是进行拆分,然后检查所有字符串,如果最后一个字符是\,则将其与下一个字符串连接。

当然,如果你需要在管道之前使用转义反斜杠,那么所有这些都会忽略会发生什么......比如“\\ |”。

总的来说,我倾向于正则表达式。

坦率地说,我更喜欢使用 FileHelpers 因为,尽管这不是逗号,但它基本上是相同的。他们有一个很棒的故事 为什么你不应该自己写这些东西


0
2018-04-28 04:42





你可以用正则表达式做到这一点。一旦您决定使用反斜杠作为转义字符,您有两个转义案例来解释:

  • 逃离管道: \|
  • 转义你想要解释的反斜杠字面意思。

这两个都可以在同一个正则表达式中完成。转义的反斜杠总是两个 \ 人物在一起。连续的,逃逸的反斜杠总是偶数 \ 字符。如果你找到一个奇数序列的 \ 在管道之前,这意味着你有几个转义的反斜杠,然后是一个转义的管道。所以你想要使用这样的东西:

/^(?:((?:[^|\\]|(?:\\{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

0
2018-04-28 04:59



@Justin由于某种原因它不能在我的电脑上工作。也 ) 不见了。 - Oscar Mederos
@Oscar - 有很多嵌套的括号,很难跟踪。现在就试试。 - Justin Morgan
@Justin现在可以使用,虽然@Cory解决方案也是如此: 一个\\ | b 应该成为 A \ | b 而不是A \\和 b。第一个\\是一个像任何其他人一样的角色,第二个是逃脱的 |,所以第二个将被删除,句子将保持原样。 - Oscar Mederos
@Oscar - 如果您输入 A\\|b,你已经转义了反斜杠字符本身,因此它应该被解释为 A` plus b. To get A \ | b, you would input 一个\\\ | b. That's how I would expect it to work, myself, and it's consistent with most escape schemes I've seen. In C#, for example, the string \\\ n`将是一个文字`和回车。 - Justin Morgan
@Justin取决于你如何服用它。有人告诉你的时候: I want to parse the string ABC\DE,你应该假设\已经被转义了。否则原始示例没有意义,因为如果写“\ |”,C#本身会出错因为你在这里逃避任何事。为了恢复,我认为要解析的字符串是文字的(已经转义)。 - Oscar Mederos