fgetcsv и fputcsv поддерживают аргумент $escape
, однако он либо сломан, либо я не понимаю, как он должен работать. Игнорируйте тот факт, что вы не видите параметр $escape
зарегистрированный на fputcsv
, он поддерживается в PHP-источнике, есть небольшая ошибка, которая препятствует его fputcsv
в документацию.
Функция также поддерживает параметры $delimiter
и $enclosure
, по умолчанию – запятую и двойную кавычку соответственно. Я бы ожидал, что параметр $escape
должен быть передан, чтобы иметь поле, содержащее любой из этих метасимволов (обратная косая черта, запятая или двойная кавычка), однако это, конечно, не так. (Теперь я понимаю, что, читая Википедию , они должны быть заключены в двойные кавычки).
Возьмем, к примеру, ловушку, которая затронула многочисленные плакаты в разделе комментариев из документации fgetcsv
. Случай, когда мы хотели бы написать одну обратную косую черту в поле.
$r = fopen('/tmp/test.csv', 'w'); fwrite($r, '"\"'); fclose($r); $r = fopen('/tmp/test.csv', 'r'); var_dump(fgetcsv($r)); fclose($r);
Это возвращает false
. Я также попробовал "\\"
, однако это также возвращает false
. Заполнение обратной косой черты с некоторым туманным текстом дает fgetcsv
импульс, который ему нужен … "hi\\there"
и "hi\there"
оба разбора и имеют одинаковый результат, но результат имеет только 1 обратную косую черту, так что точка $escape
вообще?
Я наблюдал такое же поведение, когда не включал обратную косую черту в двойных кавычках. Запись файла CSV, содержащего строку \
и \\
, имеет тот же результат при fgetcsv
, 1 обратная косая черта.
Давайте спросим PHP, как он может кодировать обратную косую черту как поле в CSV, используя fputcsv
$r = fopen('/tmp/test.csv', 'w'); fputcsv($r, array('\\')); fclose($r); echo file_get_contents('/tmp/test.csv');
Результатом является двойная кодовая замкнутая обратная косая черта (и я пробовал 3 версии PHP> 5.5.4, когда предположительно добавлялась поддержка $enclose
fputcsv
в fputcsv
). fgetcsv
этого заключается в том, что fgetcsv
не может даже прочитать его правильно в моих заметках выше, он возвращает false
… Я ожидал, что fputcsv
не будет заключать обратную косую черту в двойных кавычках или fgetcsv
, чтобы читать "\"
как fputcsv
написал это … или действительно в моем, по-видимому, неправильном мышлении, для fputcsv
чтобы fputcsv
написал двойную кавычку, заключенную пару обратных косых черт, и чтобы fgetcsv
мог правильно ее разобрать!
Попробуйте написать одну цитату в файл с помощью fputcsv
, а затем прочитать ее через fgetcsv
.
$aBackslash = array('\\'); // Write a single backslash to a file using fputcsv $r = fopen('/tmp/test.csv', 'w'); fputcsv($r, $aBackslash); fclose($r); // Read the file using fgetcsv $r = fopen('/tmp/test.csv', 'r'); $aFgetcsv = fgetcsv($r); fclose($r); // Compare the read value from fgetcsv to our original value if(count(array_diff($aBackslash, $aFgetcsv))) echo "PHP CSV support is broken\n";
Сделав шаг назад, у меня есть некоторые вопросы
$escape
? Первоначально я обнаружил это, когда сотрудник предоставил мне CSV-файл, созданный на Python, который написал одиночную обратную косую черту, заключенную в двойные кавычки, и после того, как fgetcsv
не смог ее прочитать. У меня был gaul, чтобы спросить его, может ли он использовать стандартную функцию Python. Мало ли я знаю, что инструментарий PHP CSV – запутанный беспорядок! (FWIW: разработчик Python говорит мне, что использует модуль записи CSV).
Из быстрого обзора документации Python по параметрам формата CSV escape-символ, используемый в закрытых значениях (т. Е. Внутри двойных кавычек), является другой двойной цитатой.
Для PHP escape-символом по умолчанию является обратная косая черта (^) ; чтобы соответствовать поведению Python, вам нужно использовать это:
$data = fgetcsv($r, 0, ',', '"', '"');
(^) На самом деле fgetcsv()
обрабатывает как $enclosure||$enclosure
и $escape||$enclosure
таким же образом, поэтому аргумент $escape
используется, чтобы избежать обработки обратной косой черты как специального символа.
(^^) Установка параметра $length
в 0
вместо фиксированного жесткого предела делает его менее эффективным.
Поэтому после сна и перехвата кода, оказывается, что fputcsv не принимает параметр escape, и я был глуп. Я обновил код ниже до правильного рабочего кода. Один и тот же базовый принцип применяется, параметр escape должен изменить параметр escape, чтобы вы могли загрузить CSV с обратной косой чертой, не считая их escape-символами. Хитрость заключается в использовании символа, который не содержится в csv. Вы можете сделать это, grepping файла для определенного символа, пока не найдете тот, который не возвращается.
Хорошо, поэтому вердикт заключается в том, что он проверяет escape-символ, а затем никогда не прекращает проверку. Итак, если он найдет это, это ускользнет. Так просто.
При этом цель параметра escape заключается в том, чтобы разрешить эту точную ситуацию, когда вы можете изменить символ возврата на символ, который не нужен.
Здесь я превратил ваш пример кода в рабочий код:
$aBackslash = array('\\'); // Write a single backslash to a file using fputcsv $r = fopen('/tmp/test.csv', 'w'); fputcsv($r, $aBackslash, ',', '"'); // EDIT 2: Removed escape param that causes PHP Notice. fclose($r); // Read the file using fgetcsv $r = fopen('/tmp/test.csv', 'r'); $aFgetcsv = fgetcsv($r, ',', '"', '#'); fclose($r); // Compare the read value from fgetcsv to our original value if(count(array_diff($aBackslash, $aFgetcsv))) echo "PHP CSV support is broken\n"; else echo "PHP WORKS!\n";
Одним из важных моментов является то, что как fgetcsv
и fputcsv
должны иметь одинаковые параметры, иначе возвращаемый массив не будет соответствовать исходному массиву.
Вы очень верны. Это не соответствует языку. Я пробовал каждую перестановку косых черт, о которой я могу думать, и я до сих пор не добился успешного ответа от CSV. Он всегда возвращается, как говорит ваш пример.
Я думаю, что упоминалось @deceze в том, что в вашем примере вы используете array('\\')
который на самом деле является строковым литералом «\», который PHP интерпретирует как таковой, и передает «\» в CSV, который затем возвращается путь. Это возвращает ошибочный ответ \"
, который, как я уже говорил выше, определенно ошибочен.
Мне удалось найти работу, чтобы результат был действительно уместным:
Во-первых, для вашего примера нам нужно будет сгенерировать /tmp/test.csv с «\» в качестве тела или немного изменить массив. Самый простой способ – просто изменить массив на:
array('"\\\\"');
После этого мы должны немного изменить запрос fgetcsv.
$aFgetcsv = fgetcsv($r); $aFgetcsv = array_map('stripslashes', $aFgetcsv);
Делая это, мы говорим PHP о том, чтобы удалить первую косую черту, создав таким образом строку в $ aFgetcsv "\"