我有一些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文件?基本上我只需要较少的PHP代码就需要相同的test.csv输出。
提前致谢。
答案 0 :(得分:1)
在使用数组之后,PHP中还有更多可以使它变得更加简单。就像一个数组可以表示你的文件列表一样,PHP中的其他结构也是如此。
例如,由于XML文件很可能位于特定目录和中,因此可以使用 GlobIterator 轻松表示:
$inputFiles = new GlobIterator(__DIR__ . '/*.xml');
然后你可以foreach
对他们进行讨论,我将在另一个例子中展示。
这样的列表可以简化您的处理。这很重要,因为许多程序都有某种通用的公式:输入,处理,输出。这也称为IPO或IPO + S模型。 S代表存储。在您处理输入数据的情况下,您还将存储到新文件CSV文件中,该文件也是输出(处理完成后)。
当您遵循这样的通用模型时,您可以更轻松地构建代码,并且通过更好的结构,您通常可以获得更少的代码。即使不是,代码的每个部分都更加独立,更小,这通常是您正在寻找的。 p>
在我在答案开头用 GlobIterator 显示的所述XML文件列表旁边,还有其他 Iterators 可以帮助处理XML数据。
例如,您有1-n个XML文件,其中包含0-n <phone>
个元素。您知道要处理这些<phone>
元素中的任何一个,您已经完全知道您要对它们做什么(从中提取一些数据)。那么首先列出所有XML文件中的所有<phone>
元素是不是很好?
借助生成器,可以在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>
1}}元素和产生它们。
这意味着,如果在extract_phones
内调用函数foreach
,则该循环将每个<phone>
元素设为 SimpleXMLElement :
foreach(extract_phones($inputFiles) as $phone) {
# $phone is a SimpleXMLElement here
}
所以现在你的问题要求创建CSV文件作为输出。这可以创建一个 SplFileObject 来传递输出并在处理时访问它。它基本上像传递文件句柄一样工作,就像在你的问题中那样但是它有更好的语义,允许稍后更容易地改变代码(你可以用另一个对象替换它)行为相同)。
此外,我已经在您的代码中看到了一些值得讨论的细节。您将引号编码为HTML实体:
trim(str_replace( "\"", """, $phone->title ), " ")
您最有可能这样做,因为您希望在CSV文件中包含HTML实体。但是,CSV文件不需要这样。您还希望CSV文件中的数据尽可能通用。当您转换文件格式时,不应该在以后或在电子表格应用程序中的HTML上下文中使用CSV文件。我的建议就是把它留在外面用另一个地方处理。这个更属于的地方,以及稍后的地方,例如如果您使用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());
答案 1 :(得分:0)
感谢@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);
?>