В 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 * + (?, | $)