Как проанализировать строку с разделителями-запятыми, если в поле есть запятая и скобка
У меня есть эта строка в C#
adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO
Я хочу использовать RegEx для его анализа, чтобы получить следующее:
adj_con(CL2,1,3,0)
adj_cont(CL1,1,3,0)
NG
NG/CL
5 value of CL(JK)
HO
В дополнение к приведенному выше примеру, я протестировал следующее, но все еще не могу разобрать его правильно.
"%exc.uns: 8 hours let @ = ABC, DEF", "exc_it = 1 day" , " summ=graffe ", " a,b,(c,d)"
Новый текст будет в одной строке
string mystr = @"""%exc.uns: 8 hours let @ = ABC, DEF"", ""exc_it = 1 day"" , "" summ=graffe "", "" a,b,(c,d)""";
9 ответов
string str = "adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO";
var resultStrings = new List<string>();
int? firstIndex = null;
int scopeLevel = 0;
for (int i = 0; i < str.Length; i++)
{
if (str[i] == ',' && scopeLevel == 0)
{
resultStrings.Add(str.Substring(firstIndex.GetValueOrDefault(), i - firstIndex.GetValueOrDefault()));
firstIndex = i + 1;
}
else if (str[i] == '(') scopeLevel++;
else if (str[i] == ')') scopeLevel--;
}
resultStrings.Add(str.Substring(firstIndex.GetValueOrDefault()));
Событие быстрее:
([^,]*\x28[^\x29]*\x29|[^,]+)
Это должно делать свое дело. По сути, ищите "отпечаток функции" или что-нибудь без запятой.
adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO
^ ^ ^ ^ ^
Каретки символизируют, где группировка останавливается.
Еще один способ реализовать то, что делал Snowbear:
public static string[] SplitNest(this string s, char src, string nest, string trg)
{
int scope = 0;
if (trg == null || nest == null) return null;
if (trg.Length == 0 || nest.Length < 2) return null;
if (trg.IndexOf(src) >= 0) return null;
if (nest.IndexOf(src) >= 0) return null;
for (int i = 0; i < s.Length; i++)
{
if (s[i] == src && scope == 0)
{
s = s.Remove(i, 1).Insert(i, trg);
}
else if (s[i] == nest[0]) scope++;
else if (s[i] == nest[1]) scope--;
}
return s.Split(trg);
}
Идея состоит в том, чтобы заменить любой не вложенный разделитель другим разделителем, который затем можно использовать с обычным string.Split()
, Вы также можете выбрать, какой тип кронштейна использовать - ()
, <>
, []
или даже что-то странное, как \/
, ][
, или же `'
, Для ваших целей вы бы использовали
string str = "adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO";
string[] result = str.SplitNest(',',"()","~");
Функция сначала превратит вашу строку в
adj_con(CL2,1,3,0)~adj_cont(CL1,1,3,0)~NG~ NG/CL~ 5 value of CL(JK)~ HO
затем разделить на ~
, игнорируя вложенные запятые.
Просто это регулярное выражение:
[^,()]+(\([^()]*\))?
Тестовый пример:
var s= "adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO";
Regex regex = new Regex(@"[^,()]+(\([^()]*\))?");
var matches = regex.Matches(s)
.Cast<Match>()
.Select(m => m.Value);
возвращается
adj_con(CL2,1,3,0)
adj_cont(CL1,1,3,0)
NG
NG/CL
5 value of CL(JK)
HO
Если вам просто нужно использовать Regex, вы можете разбить строку на следующее:
, # match a comma
(?= # that is followed by
(?: # either
[^\(\)]* # no parens at all
| # or
(?: #
[^\(\)]* # ...
\( # (
[^\(\)]* # stuff in parens
\) # )
[^\(\)]* # ...
)+ # any number of times
)$ # until the end of the string
)
Это разбивает ваш вклад в следующее:
adj_con(CL2,1,3,0)
adj_cont(CL1,1,3,0)
NG
NG/CL
5 value of CL(JK)
HO
Вы также можете использовать сбалансированные группирующие конструкции.NET для создания версии, которая работает с вложенными паренами, но вы, вероятно, также хорошо справляетесь с одним из решений не-Regex.
Класс TextFieldParser ( msdn), кажется, имеет встроенную функциональность:
Класс TextFieldParser: - Предоставляет методы и свойства для анализа структурированных текстовых файлов.
Разбор текстового файла с помощью TextFieldParser аналогичен итерации по текстовому файлу, а метод ReadFields для извлечения текстовых полей аналогичен разбиению строк.
TextFieldParser может анализировать файлы двух типов: с разделителями или с фиксированной шириной. Некоторые свойства, такие как Delimiters и HasFieldsEnclosedInQuotes, имеют смысл только при работе с файлами с разделителями, в то время как свойство FieldWidths имеет смысл только при работе с файлами фиксированной ширины.
Смотрите статью, которая помогла мне найти
var s = "adj_con(CL2,1,3,0),adj_cont(CL1,1,3,0),NG, NG/CL, 5 value of CL(JK), HO";
var result = string.Join(@"\n",Regex.Split(s, @"(?<=\)),|,\s"));
Шаблон соответствует для) и исключает его из совпадения, затем сопоставляет или сопоставляет с последующим пробелом.
результат =
adj_con (CL2,1,3,0)
adj_cont (CL1,1,3,0)
NG
NG / CL
5 значение CL (JK)
HO
Вот более сильный вариант, который разбирает весь текст, включая вложенные скобки:
string pattern = @"
\A
(?>
(?<Token>
(?:
[^,()] # Regular character
|
(?<Paren> \( ) # Opening paren - push to stack
|
(?<-Paren> \) ) # Closing paren - pop
|
(?(Paren),) # If inside parentheses, match comma.
)*?
)
(?(Paren)(?!)) # If we are not inside parentheses,
(?:,|\Z) # match a comma or the end
)*? # lazy just to avoid an extra empty match at the end,
# though it removes a last empty token.
\Z
";
Match match = Regex.Match(data, pattern, RegexOptions.IgnorePatternWhitespace);
Вы можете получить все совпадения, перебирая match.Groups["Token"].Captures
,
Предполагая, что не вложенные, совпадающие скобки, вы можете легко сопоставить нужные токены вместо разделения строки:
MatchCollection matches = Regex.Matches(data, @"(?:[^(),]|\([^)]*\))+");