В PHP у меня есть следующая строка:
$str = "AAA, BBB, (CCC,DDD), 'EEE', 'FFF,GGG', ('HHH','III'), (('JJJ','KKK'), LLL, (MMM,NNN)) , OOO";
Мне нужно разбить эту строку на следующие части:
AAA BBB (CCC,DDD) 'EEE' 'FFF,GGG' ('HHH','III') (('JJJ','KKK'),LLL, (MMM,NNN)) OOO,AAA BBB (CCC,DDD) 'EEE' 'FFF,GGG' ('HHH','III') (('JJJ','KKK'),LLL, (MMM,NNN)) OOO
Я попробовал несколько регулярных выражений, но не смог найти решение. Есть идеи?
ОБНОВИТЬ
Я решил использовать regex на самом деле не лучшее решение, имея дело с искаженными данными, скрытыми кавычками и т. Д.
Благодаря предложениям, сделанным здесь, я нашел функцию, которая использует синтаксический анализ, который я переписал в соответствии с моими потребностями. Он может обрабатывать различные типы скобок, а разделитель и цитата – это параметры.
function explode_brackets($str, $separator=",", $leftbracket="(", $rightbracket=")", $quote="'", $ignore_escaped_quotes=true ) { $buffer = ''; $stack = array(); $depth = 0; $betweenquotes = false; $len = strlen($str); for ($i=0; $i<$len; $i++) { $previouschar = $char; $char = $str[$i]; switch ($char) { case $separator: if (!$betweenquotes) { if (!$depth) { if ($buffer !== '') { $stack[] = $buffer; $buffer = ''; } continue 2; } } break; case $quote: if ($ignore_escaped_quotes) { if ($previouschar!="\\") { $betweenquotes = !$betweenquotes; } } else { $betweenquotes = !$betweenquotes; } break; case $leftbracket: if (!$betweenquotes) { $depth++; } break; case $rightbracket: if (!$betweenquotes) { if ($depth) { $depth--; } else { $stack[] = $buffer.$char; $buffer = ''; continue 2; } } break; } $buffer .= $char; } if ($buffer !== '') { $stack[] = $buffer; } return $stack; }
Вместо preg_split сделайте preg_match_all :
$str = "AAA, BBB, (CCC,DDD), 'EEE', 'FFF,GGG', ('HHH','III'), (('JJJ','KKK'), LLL, (MMM,NNN)) , OOO"; preg_match_all("/\((?:[^()]|(?R))+\)|'[^']*'|[^(),\s]+/", $str, $matches); print_r($matches);
будет печатать:
массив
(
[0] => Массив
(
[0] => AAA
[1] => BBB
[2] => (CCC, DDD)
[3] => 'EEE'
[4] => 'FFF, GGG'
[5] => ('HHH', 'III')
[6] => (('JJJ', 'KKK'), LLL, (MMM, NNN))
[7] => ООО
)
)
Регулярное выражение \((?:[^()]|(?R))+\)|'[^']*'|[^(),\s]+ может быть разделено на три части:
\((?:[^()]|(?R))+\) , которая соответствует сбалансированным парам круглых скобок '[^']*' соответствие строки с кавычками [^(),\s]+ которая соответствует любой char-последовательности, не состоящей из символов '(' , ')' , ',' или white-space Спартанское регулярное выражение, которое символизирует и также проверяет все токены, которые он извлекает:
\G\s*+((\((?:\s*+(?2)\s*+(?(?!\)),)|\s*+[^()',\s]++\s*+(?(?!\)),)|\s*+'[^'\r\n]*+'\s*+(?(?!\)),))++\))|[^()',\s]++|'[^'\r\n]*+')\s*+(?:,|$)
Regex101
Поместите его в строковый литерал с разделителем:
'/\G\s*+((\((?:\s*+(?2)\s*+(?(?!\)),)|\s*+[^()\',\s]++\s*+(?(?!\)),)|\s*+\'[^\'\r\n]*+\'\s*+(?(?!\)),))++\))|[^()\',\s]++|\'[^\'\r\n]*+\')\s*+(?:,|$)/'
ideone
Результат заключается в захвате группы 1. В примере на ideone я указываю флаг PREG_OFFSET_CAPTURE , чтобы вы могли проверить против последнего совпадения в группе 0 (полное совпадение), была ли потреблена вся исходная строка или нет.
\s . Следовательно, он может не охватывать несколько строк. ( , ) , ' или. ' . , ( и заканчивается ) . () не допускается. \s , который включает в себя новый символ строки) произвольно разрешен между токенами, запятыми (-ами) , разделительными маркерами и скобками (-ами) ( , ) токенов-скобок.
\ G \ s * +
(
(
\ (
(?:
\ S * +
(? 2)
\ S * +
((?! \)))
|
\ S * +
[^ ()», \ S] ++
\ S * +
((?! \)))
|
\ S * +
'[^' \ Г \ п] * +»
\ S * +
((?! \)))
) ++
\)
)
|
[^ ()», \ S] ++
|
'[^' \ Г \ п] * +»
)
\ S * + (?, | $)