2011-04-28 72 views
10

我有一个管道分隔文件,我想拆分(我正在使用C#)。例如:C# - 在数据中使用逃逸管道分割管道?

 
This|is|a|test 

但是,其中一些数据可以包含一个管道。

 
This|is|a|pip\|ed|test (this is a pip|ed test) 

,我不知道是否有一个正则表达式或一些其他方法来分开上只是“纯”管道(也就是拆分此,有管道:如果是这样,它会用反斜杠转义在他们面前没有反斜杠)。我目前的方法是用自定义的文本位置来代替转义的管道,在管道上分开,然后用管道替换我的自定义文本。不是很优雅,我不禁觉得有更好的方法。谢谢你的帮助。

+0

你见过[this(monster)thread](http://stackoverflow.com/questions/2148587/regex-quoted-string-with-escaped-quotes-in-c)。不是一个直接的答案,但希望推动正确的方向。 – dawebber 2011-04-28 04:32:58

+0

如果你想在其中一块的末尾有文字反斜杠怎么办? – Random832 2011-04-28 04:42:28

回答

6

只需使用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; 
} 
+0

是的,这比使用'regex'更好地解析'string'。这运行得更快。 +1 – KaeL 2011-04-28 04:39:45

+0

如果您不将这些单词添加到“List ”并返回它,手动解析方法的速度比正则表达式快5倍。如果你重新加入管理'List '的开销,那么在我的机器上它的速度要快3倍。 – 2011-04-28 14:55:27

+0

@Cory:有趣。谢谢! – 2011-04-28 15:08:32

3

这现在应该做的:

string test = @"This|is|a|pip\|ed|test (this is a pip|ed test)"; 
string[] parts = Regex.Split(test, @"(?<!(?<!\\)*\\)\|"); 

正则表达式基本上说:分割上不是由前跟转义字符管道。我不应该为此承担任何功劳,我只是劫持了正则表达式from this post并简化了它。

EDIT

在性能方面,相对于在这个线程提供的手册解析方法中,我发现,此正则表达式实现比使用所提供的较长的测试串乔纳森伍德的实施慢3〜5倍OP。这样说,如果你没有实例化或添加单词到List<string>并返回void代替,Jon的方法比单纯分裂的方法(0.01ms对0.002ms)快5倍左右在字符串上。如果加回管理和返回List<string>的开销,它大约快3.6倍(0.01ms vs. 0.00275ms),平均在几百万次迭代中。我没有使用静态Regex.Split()进行此测试,而是使用上面的表达式创建了一个新的Regex实例,它位于我的测试循环之外,然后调用它的Split方法。

UPDATE

使用静态Regex.Split()函数实际上是很多不是重用表达的一个实例更快。在这个实现中,正则表达式的使用仅比Jon的实现慢了大约1.6倍(0.0043ms vs. 0.00275ms)

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

+2

假设反斜杠也可以被转义(例如“This | is | a | pip \ | | test |(这是一个点测试)”),这是行不通的。你需要使用提到的帖子中的完整版。 – porges 2011-04-28 04:56:28

+0

@你是对的Porges。这是我第一次想到当我决定写一些关于它的代码:) – 2011-04-28 05:01:32

0

科里的解决方案是相当不错的。但是,如果我不喜欢使用正则表达式,那么您可以简单地执行搜索“\ |”的操作并用其他字符替换它,然后进行拆分,然后再用“\ |”替换它。

另一个选择是做分割,然后检查所有的字符串,如果最后一个字符是\,然后加入下一个字符串。

当然,所有这些都忽略了如果在管道之前需要一个转义反斜杠会发生什么......就像“\\ ​​|”。

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

坦率地说,我更喜欢使用FileHelpers,因为尽管这不是逗号定界,但它基本上是一样的。他们有一个关于why you shouldn't write this stuff yourself的好故事。

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

这很漂亮,但如果你关心性能,这不是一个很好的选择。 – 2011-04-28 04:54:25

+0

@Jonathan正如我所说,这只是另一种做法。粘贴与您提供的代码类似的代码没有任何意义。我同意你的看法,虽然表现可能并不是我在这个问题上真正重要的一点。 – 2011-04-28 04:59:13

+0

我不是在批评你发布它。事实上,我提到它很光滑。我只是在评论这种方法的表现。 – 2011-04-28 06:05:34

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 
+0

@Justin出于某种原因,它不适用于我的电脑。另外,'''丢失。 – 2011-04-28 05:03:57

+0

@Oscar - 有很多嵌套的括号很难跟踪。现在就试试。 – 2011-04-28 05:07:14

+0

@Justin现在可以工作,虽然它与@Cory解决方案一样:** A \\ | b **应该变成** A \ | b **而不是A \\和** b **。第一个是一个像其他任何角色一样的角色,第二个角色是逃避** | **,所以第二个角色将被移除,并且句子将保持原样。 – 2011-04-28 05:10:41

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