Ну, привет, сообщество. Я работаю над декодером CSV на PHP (да, я знаю, что он уже есть, но для меня это вызов, так как я изучаю его в свободное время). Теперь проблема: ну, строки разделены на PHP_EOL
,
В этой строке:
foreach(explode($sep, $str) as $line) {
где сентябрь переменная, которая разделяет строки и ул Строка, которую я хочу расшифровать.
Но если я хочу разделить столбцы точкой с запятой, может возникнуть ситуация, когда точка с запятой — это содержимое одного столбца. И, как я исследовал, эта проблема решается путем окружения всего столбца кавычками:
Входные данные:
"0;0";1;2;3;4
Ожидаемый результат:
0; 0 | 1 | 2 | 3 | 4
Я уже думал о взгляде вперед / назад. Но так как я не использовал его в прошлом, и, возможно, это может быть хорошей практикой, я не знаю, как включить его в регулярное выражение. Моя функция декодирования возвращает 2D-массив (например, таблицу …), и я подумал добавить строки в массив следующим образом (Да, регулярное выражение f *** ed …):
$res[] = preg_split("/(?<!\")". preg_quote($delim). "(?!\")/", $line);
И наконец мой полный код:
function csv_decode($str, $delim = ";", $sep = PHP_EOL) {
if($delim == "\"") $delim = ";";
$res = [];
foreach(explode($sep, $str) as $line) {
$res[] = preg_split("/(?<!\")". preg_quote($delim). "(?!\")/", $line);
}
return $res;
}
Заранее спасибо!
Это немного нелогично, но самый простой способ разбить строку с помощью регулярных выражений — это часто использовать preg_match_all
на месте preg_split
:
preg_match_all('~("[^"]*"|[^;"]*)(?:;|$)~A', $line, $m);
$res[] = $m[1];
Модификатор A обеспечивает непрерывность последовательных совпадений с начала строки.
Если вы не хотите, чтобы цитаты были включены в результат, вы можете использовать функция сброса ветви (?|..(..)..|..(..)..)
:
preg_match_all('~(?|"([^"]*)"|([^;"]*))(?:;|$)~A', $line, $m);
Другой обходной путь, но на этот раз для preg_split
: включите часть, которую вы хотите избежать, перед разделителем и отбросьте ее из всего соответствия, используя \K
особенность:
$res[] = preg_split('~(?:"[^"]*")?\K;~', $line);
Вы можете использовать эту функцию str_getcsv
в этом вы можете указать пользовательский разделитель (;
) также.
<?php
$string='"0;0";1;2;3;4';
print_r(str_getcsv($string,";"));
Выход:
Array
(
[0] => 0;0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
)
Разделение не является хорошим выбором для линий типа CSV.
Вы могли бы использовать старый проверенный и верный \G
якорь с находкой глобально типа func.
Regex: '~\G(?:(?:^|;)\s*)(?|"([^"]*)"|([^;]*?))(?:\s*(?:(?=;)|$))~'
Информация:
\G # G anchor, start where last match left off
(?: # leading BOL or ;
(?: ^ | ; )
\s* # optional whitespaces
)
(?| # branch reset
"( [^"]* ) # (1), double quoted string data
"| # or
( [^;]*? ) # (1), non-quoted field
)
(?: # trailing optional whitespaces
\s*
(?:
(?= ; ) # lookahead for ;
| $ # or EOL
)
)