У меня есть несколько XML-файлов, которые имеют те же элементы, но только с разной информацией.
Первый файл test.xml
<?xml version="1.0" encoding="UTF-8"?>
<phones>
<phone>
<title>"Apple iPhone 5S"</title>
<price>
<regularprice>500</regularprice>
<saleprice>480</saleprice>
</price>
<color>black</color>
</phone>
</phones>
Второй файл test1.xml
<?xml version="1.0" encoding="UTF-8"?>
<phones>
<phone>
<title>Nokia Lumia 830</title>
<price>
<regularprice>400</regularprice>
<saleprice>370</saleprice>
</price>
<color>black</color>
</phone>
</phones>
Мне нужно преобразовать некоторые значения из этих файлов XML в 1 файл test.csv
Так что я использую этот код PHP
<?php
$filexml1='test.xml';
$filexml2='test1.xml';
//File 1
if (file_exists($filexml1)) {
$xml = simplexml_load_file($filexml1);
$f = fopen('test.csv', 'w');
$headers = array('title', 'color');
$converted_array = array_map("strtoupper", $headers);fputcsv($f, $converted_array, ',', '"');foreach ($xml->phone as $phone) {
//$phone->title = trim($phone->title, " ");
// Array of just the components you need...
$values = array(
"title" => (string)$phone->title = trim(str_replace ( "\"", """, $phone->title ), " "),
"color" => (string)$phone->color
);
fputcsv($f, $values,',','"');
}
fclose($f);
echo "<p>File 1 coverted to .csv sucessfully</p>";
} else {
exit('Failed to open test.xml.');
}
//File 2
if (file_exists($filexml2)) {
$xml = simplexml_load_file($filexml2);
$f = fopen('test.csv', 'a');//the same code for second file like for the first file
echo "<p>File 2 coverted to .csv sucessfully</p>";
} else {
exit('Failed to open test1.xml.');
}
?>
Вывод test.csv выглядит следующим образом
TITLE COLOR
Apple iPhone 5S black
Nokia Lumia 830 black
Как видите, мне удалось загрузить каждый файл только в переменную, и для каждого файла мне нужно написать оператор if, который делает скрипт слишком большим, поэтому мне интересно, можно ли загрузить все файлы в массив, обработать их одним блок кода, потому что элементы XML одинаковы и выводятся в один файл .csv? По сути, мне нужен тот же вывод test.csv только с меньшим количеством PHP-кода.
Заранее спасибо.
Помимо использования массива, в PHP есть еще кое-что, что может сделать его еще проще. Как массив может представлять список ваших файлов, так и другие конструкции в PHP тоже могут.
Например, поскольку ваши файлы XML, скорее всего, находятся в определенном каталоге а также следуйте некоторому шаблону с их именем файла, они могут быть легко представлены GlobIterator:
$inputFiles = new GlobIterator(__DIR__ . '/*.xml');
Вы могли бы тогда foreach
над ними, что я покажу через другой пример.
Такой список позволяет упростить обработку. Это важно, потому что для многих программ существует некая общая форма: ввод, обработка, вывод. Это также называется IPO или IPO + S Model. S обозначает хранение. В вашем случае во время обработки входных данных вы также сохраняете в новый файл CSV-файл, который также является выходным (после полной обработки).
Когда вы следуете такой общей модели, вам легче структурировать свой код, и с лучшей структурой у вас чаще всего меньше кода. Даже если нет, каждая часть вашего кода более автономна и меньше, что чаще всего является тем, что вы ищете.
Рядом с указанным списком XML-файлов я показал в начале ответа GlobIterator Есть и другие итераторы это может помочь обработать данные XML.
Например, у вас есть 1-n XML-файлов, которые содержат 0-n <phone>
элементы. Вы знаете, что хотите обработать любой из этих <phone>
элементы, вы уже именно так знать, что вы хотите с ними сделать (извлечь из него некоторые данные). Так что не было бы здорово иметь список всех <phone>
элементы во всех XML-файлах в первую очередь?
Это может быть легко сделано в PHP с помощью Генератор. Это функция, которая может возвращать значения несколько раз, пока она еще «работает». Это упрощение, лучше покажите код, чтобы проиллюстрировать это. Допустим, у нас есть список файлов XML в качестве входных данных, и мы хотим, чтобы все <phone>
элементы из этого. Конечно, вы можете создать массив всех этих <phone>
элементы и обработать этот массив позже. Тем не менее, Генератор может предложить все это <phone>
элементы непосредственно для использования в foreach
цикл:
function extract_phones(Traversable $files) {
foreach ($files as $file) {
$xml = simplexml_load_file($file);
if ($xml === false) {
continue;
}
foreach ($xml->phone as $phone) {
yield $phone;
}
}
}
Как это образцово Генератор функция показывает, она проходит через все $files
пытается загрузить их как SimpleXMLElement и если успешно, перебирает все <phone>
элементы и доходность их.
Это означает, что если функция extract_phones
называется внутри foreach
этот цикл будет иметь каждый <phone>
элемент как SimpleXMLElement:
foreach(extract_phones($inputFiles) as $phone) {
# $phone is a SimpleXMLElement here
}
Итак, теперь ваш вопрос задает вопрос о создании файла CSV в качестве вывода. Это можно сделать, создав SplFileObject передать вывод и получить доступ к нему во время обработки. По сути, это работает так же, как передача дескриптора файла, как вы делаете в своем вопросе но он имеет лучшую семантику, которая позволяет позже легче изменять код (вы можете заменить его другим объектом, который ведет себя так же).
Кроме того, я увидел небольшую деталь в вашем коде, которая стоит обсудить сначала. Вы кодируете кавычки как объекты HTML:
trim(str_replace( "\"", """, $phone->title ), " ")
Скорее всего, вы делаете это, потому что вы хотите, чтобы HTML-сущности были внутри CSV-файла. Однако файл CSV не нуждается в таком. Вы также хотите, чтобы данные в CSV-файле были как можно более общими. Будет ли файл CSV использоваться позже в контексте HTML или в приложении для работы с электронными таблицами, не должно быть проблемой при преобразовании формата файла. Мое предложение здесь, чтобы оставить это и разобраться с этим в другом месте. Место, к которому больше принадлежит это, а потом, например, если вы используете данные из CSV, создавая некоторый HTML.
Это сохраняет ваше преобразование и данные чистыми, а также удаляет подробные места в вашей обработке, которые не только усложняют код, но очень часто являются местом, где мы вносим недостатки в наши программы.
Я для себя просто уберу это из моего примера.
Итак, давайте соберем все это вместе: получите все телефоны из всех файлов XML и сохраните интересующие поля в выходной CSV-файл:
$files = new GlobIterator(__DIR__ . '/*.xml');
$phones = extract_phones($files);
$output = new SplFileObject('file.csv', 'w');
$output->fputcsv($header = ["title", "color"]);
foreach ($phones as $phone) {
$output->fputcsv(
[
$phone->title,
$phone->color,
]
);
}
Затем создается искомый выходной файл (без HTML-сущностей):
title,color
"""Apple iPhone 5S""",black
"Nokia Lumia 830",black
Все, что для этого нужно — это функция генератора, которую я уже показал выше, которая сама по себе также имеет простой код. Все остальное уже поставляется с PHP. Вот пример кода полностью:
<?php
/**
* @link http://stackoverflow.com/questions/26074850/convert-multiple-xml-files-to-csv-with-simplexml
*/
function extract_phones(Traversable $files)
{
foreach ($files as $file) {
$xml = simplexml_load_file($file);
if ($xml === false) {
continue;
}
foreach ($xml->phone as $phone) {
yield $phone;
}
}
}
$files = new GlobIterator(__DIR__ . '/*.xml');
$phones = extract_phones($files);
$output = new SplFileObject('file.csv', 'w');
$output->fputcsv($header = ["title", "color"]);
foreach ($phones as $phone) {
$output->fputcsv(
[
$phone->title,
$phone->color,
]
);
}
echo file_get_contents($output->getFilename());
Спасибо @Ghost за указание мне в правильном направлении. Так вот мое решение.
<?php
$filexml = array ('test.xml', 'test1.xml');//Headers
$fp = fopen('file.csv', 'w');
$headers = array('title', 'color');
$converted_array = array_map("strtoupper", $headers);fputcsv($fp, $converted_array, ',', '"');//XML
foreach ($filexml as $file) {
if (file_exists($file)) {
$xml = simplexml_load_file($file);
foreach ($xml->phone as $phone) {
$values = array(
"title" => (string)$phone->title = trim(str_replace ( "\"", """, $phone->title ), " "),
"color" => (string)$phone->color
);
fputcsv($fp, $values, ',', '"');
}
echo $file . ' converted to .csv sucessfully' . '<br>';
} else {
echo $file . ' was not found' . '<br>';
}}
fclose($fp);
?>