当我只有一个平衡角色时,我已经通过平衡 Regex 来做到这一点......但是有了更多平衡角色,它变得更加复杂和丑陋。
就我目前的目的而言,我改为编写了一个方法来通过对字符串进行标记来做到这一点,但它非常慢(而且效率非常低)。最昂贵的部分似乎是我正在做的无偿使用子字符串(是的,我知道这很糟糕)。
基本上,我想采取以下
hello("(abc d)", efg (hijk)) and,some more<%lmn, "o(\")pq", (xy(z))%>
最后得到
hello("(abc d)", efg (hijk))
[space] (the actual character)
and
,
some more
<%lmn, "o()pq", (xy(z))%>
换句话说,我正在拆分(我希望这些包含在数组结果中)
[space]
,
....我有“平衡的分组字符串”
" "
( )
<% %>
...而且我有转义字符
\
我不想为此目的编写一个完整的大解析器......
这是代码:
public static IEnumerable<string> SplitNotEnclosed(this string s, IEnumerable<string> separators, Dictionary<string, string> enclosingValues = null, IEnumerable<char> escapeCharacters = null, bool includeSeparators = false, StringComparison comparisonType = StringComparison.Ordinal)
{
var results = new List<string>();
var enclosureStack = new Stack<KeyValuePair<string, string>>();
bool atEscapedCharacter = false;
if (escapeCharacters == null) escapeCharacters = new[] { '\\' };
if (enclosingValues == null) enclosingValues = new[] { "\"" }.ToDictionary(i => i);
var orderedEnclosingValues = enclosingValues.OrderByDescending(i => i.Key.Length).ToArray();
separators = separators.OrderByDescending(v => v.Length).ToArray();
var currentPart = new StringBuilder();
while (s.Length > 0)
{
int addToIndex = 0;
var newEnclosingValue = orderedEnclosingValues.FirstOrDefault(v => s.StartsWith(v.Key, comparisonType));
if (enclosureStack.Count > 0 && !atEscapedCharacter && s.StartsWith(enclosureStack.Peek().Value))
{
addToIndex = enclosureStack.Peek().Value.Length;
enclosureStack.Pop();
}
else if (newEnclosingValue.Key != null && !atEscapedCharacter)
{
enclosureStack.Push(newEnclosingValue);
addToIndex = newEnclosingValue.Key.Length;
}
else if (escapeCharacters.Contains(s[0]) && enclosureStack.Count > 0)
{
atEscapedCharacter = !atEscapedCharacter;
addToIndex = 1;
}
else if (enclosureStack.Count > 0)
{
atEscapedCharacter = false;
addToIndex = 1;
}
if (enclosureStack.Count == 0)
{
string separator = separators.FirstOrDefault(v => s.StartsWith(v, comparisonType));
if (separator != null)
{
if (currentPart.Length > 0) results.Add(currentPart.ToString());
results.Add(separator);
s = s.Substring(separator.Length);
currentPart = new StringBuilder();
addToIndex = 0;
}
else
{
addToIndex = 1;
}
}
currentPart.Append(s.Substring(0, addToIndex));
s = s.Substring(addToIndex);
}
if (currentPart.Length > 0) results.Add(currentPart.ToString());
if (!includeSeparators)
{
results = results.Except(separators).ToList();
}
return results.ToArray();
}