Php 使用XMLReader读取大于1GB的文件时出现问题
是否存在XMLReader可以处理的最大文件大小 我正在尝试处理一个大约3GB的XML提要。当然没有PHP错误,因为脚本运行良好,并在运行后成功加载到数据库 该脚本在较小的测试源(1GB及以下)中也可以正常运行。但是,当处理较大的提要时,脚本在大约1GB后停止读取XML文件,并继续运行脚本的其余部分 有没有人经历过类似的问题?如果是的话,你是如何解决的Php 使用XMLReader读取大于1GB的文件时出现问题,php,file,size,max,xmlreader,Php,File,Size,Max,Xmlreader,是否存在XMLReader可以处理的最大文件大小 我正在尝试处理一个大约3GB的XML提要。当然没有PHP错误,因为脚本运行良好,并在运行后成功加载到数据库 该脚本在较小的测试源(1GB及以下)中也可以正常运行。但是,当处理较大的提要时,脚本在大约1GB后停止读取XML文件,并继续运行脚本的其余部分 有没有人经历过类似的问题?如果是的话,你是如何解决的 提前感谢。我在解析大型文档时遇到了类似的问题。最后我做的是使用文件系统函数将提要分解成更小的块,然后解析那些更小的块。。。因此,如果您有一堆正在
提前感谢。我在解析大型文档时遇到了类似的问题。最后我做的是使用文件系统函数将提要分解成更小的块,然后解析那些更小的块。。。因此,如果您有一堆正在解析的
标记,请使用字符串函数作为流解析它们,当您在缓冲区中获得完整记录时,使用xml函数解析它们。。。这很糟糕,但它工作得很好(而且非常节省内存,因为您在任何时候的内存中最多只有一条记录).拆分文件肯定会有帮助。其他要尝试的东西
根据您的操作系统,您可以分配的RAM块也可能有2gb的限制。如果您在32位操作系统上运行,这是很有可能的。您是否发现任何错误
libxml_use_internal_errors(true);
libxml_clear_errors();
// your parser stuff here....
$r = new XMLReader(...);
// ....
foreach( libxml_get_errors() as $err ) {
printf(". %d %s\n", $err->code, $err->message);
}
当解析器过早停止时 使用WindowsXP、NTFS作为文件系统和PHP5.3.2,此测试脚本没有问题
<?php
define('SOURCEPATH', 'd:/test.xml');
if ( 0 ) {
build();
}
else {
echo 'filesize: ', number_format(filesize(SOURCEPATH)), "\n";
timing('read');
}
function timing($fn) {
$start = new DateTime();
echo 'start: ', $start->format('Y-m-d H:i:s'), "\n";
$fn();
$end = new DateTime();
echo 'end: ', $start->format('Y-m-d H:i:s'), "\n";
echo 'diff: ', $end->diff($start)->format('%I:%S'), "\n";
}
function read() {
$cnt = 0;
$r = new XMLReader;
$r->open(SOURCEPATH);
while( $r->read() ) {
if ( XMLReader::ELEMENT === $r->nodeType ) {
if ( 0===++$cnt%500000 ) {
echo '.';
}
}
}
echo "\n#elements: ", $cnt, "\n";
}
function build() {
$fp = fopen(SOURCEPATH, 'wb');
$s = '<catalogue>';
//for($i = 0; $i < 500000; $i++) {
for($i = 0; $i < 60000000; $i++) {
$s .= sprintf('<item>%010d</item>', $i);
if ( 0===$i%100000 ) {
fwrite($fp, $s);
$s = '';
echo $i/100000, ' ';
}
}
$s .= '</catalogue>';
fwrite($fp, $s);
flush($fp);
fclose($fp);
}
(如您所见,我把结束时间的输出搞砸了,但我不想再运行这个脚本7分钟以上;-))
这也适用于您的系统吗
附带说明:相应的C#test应用程序只花了41秒,而不是7,5分钟。在这种情况下,我的硬盘速度慢可能是一个限制因素
filesize: 1.380.000.023
start: 2010-08-07 09:55:24
........................................................................................................................
#elements: 60000001
end: 2010-08-07 09:56:05
diff: 00:41
资料来源:
using System;
using System.IO;
using System.Xml;
namespace ConsoleApplication1
{
class SOTest
{
delegate void Foo();
const string sourcepath = @"d:\test.xml";
static void timing(Foo bar)
{
DateTime dtStart = DateTime.Now;
System.Console.WriteLine("start: " + dtStart.ToString("yyyy-MM-dd HH:mm:ss"));
bar();
DateTime dtEnd = DateTime.Now;
System.Console.WriteLine("end: " + dtEnd.ToString("yyyy-MM-dd HH:mm:ss"));
TimeSpan s = dtEnd.Subtract(dtStart);
System.Console.WriteLine("diff: {0:00}:{1:00}", s.Minutes, s.Seconds);
}
static void readTest()
{
XmlTextReader reader = new XmlTextReader(sourcepath);
int cnt = 0;
while (reader.Read())
{
if (XmlNodeType.Element == reader.NodeType)
{
if (0 == ++cnt % 500000)
{
System.Console.Write('.');
}
}
}
System.Console.WriteLine("\n#elements: " + cnt + "\n");
}
static void Main()
{
FileInfo f = new FileInfo(sourcepath);
System.Console.WriteLine("filesize: {0:N0}", f.Length);
timing(readTest);
return;
}
}
}
应该注意的是,PHP通常有一个最大文件大小。PHP不允许无符号整数或长整数,这意味着整数的上限为2^31(对于64位系统为2^63)。这一点很重要,因为PHP使用整数作为文件指针(您在文件中读取时的位置),这意味着它无法处理大于2^31字节的文件
但是,这应该超过1GB。我遇到了2G字节的问题(正如预期的那样,因为2^31大约是20亿)。最近我遇到了类似的问题,我想分享一下我的经验 问题似乎在于PHP的编译方式,无论是支持64位文件大小/偏移量还是仅支持32位文件大小/偏移量 32位只能寻址4GB的数据。您可以在这里找到一些令人困惑但很好的解释: 我必须使用Perl实用工具
xml\u split
拆分文件,您可以在这里找到:
我用它将巨大的XML文件分割成可管理的块。该工具的优点在于它将XML文件拆分为整个元素。不幸的是,它不是很快
我只需要做一次,它适合我的需要,但我不建议重复使用。拆分后,我在大小约为
1GB
的较小文件上使用了XMLReader。您确定没有生成PHP错误吗?工作和不工作之间的决定性因素究竟是什么(据你所知)?“脚本”看起来像什么,除了在XML上迭代之外,它还做了什么?在伪代码中,脚本看起来像$this->downloadFeed();尝试{$this->writeXMLFeedToCSV();}catch(e){//handle exception}$this->uploadCSVToDatabaseTable();如果脚本由于PHP错误而失败,它将不会上载到数据库。目前确实如此。xml的格式也是正确的,就像ircmaxell所说的,当脚本崩溃时,它可以正常工作。然而,这个过程是乏味的,我们希望找到一个解决办法。很抱歉,由于信息的性质,我不能随意分享脚本。您使用哪一个操作系统b)文件系统c)php版本d)php版本进行测试?谢谢,是的,我最后也是这么做的。但是,正如您所提到的,它很糟糕:o)您是否碰巧知道xml阅读器是否有可以读取的最大文件大小?再次感谢您的建议,我发现了错误的来源和一个迄今为止一直对我有效的解决方案,并认为您可能能够实现它。事实证明,提要中有一个垂直选项卡(^K或char 11),它不是无效字符,但对于我使用的文档类型无效。在处理提要之前,我通过sed find和replace运行了提要,并且能够解析大于2gb的字段。感谢大家的建议。XMLReader接口应该像SAX解析器一样按顺序处理大型文档,也就是说,它不(一定)将整个文档加载到内存中。谢谢。已经调整了内存。沃尔克也是对的。XMLReader以与SAX解析器类似的方式读取。如果其他方法都失败了,我将尝试使用SAX,但我宁愿不重写脚本。我正在编写一个脚本的独立副本,这可能会进一步说明这个问题,但我很确定XML或PHP脚本本身没有问题。只要文件小于1GB,它就可以按预期的方式运行,没有问题。即使更大,它也运行良好,只是无法读取所有xml。不过,感谢您的建议。“但我很确定这不是XML或PHP脚本本身的问题。”-只是为了确保:libxml_get_errors()这件事并不意味着scri有问题
using System;
using System.IO;
using System.Xml;
namespace ConsoleApplication1
{
class SOTest
{
delegate void Foo();
const string sourcepath = @"d:\test.xml";
static void timing(Foo bar)
{
DateTime dtStart = DateTime.Now;
System.Console.WriteLine("start: " + dtStart.ToString("yyyy-MM-dd HH:mm:ss"));
bar();
DateTime dtEnd = DateTime.Now;
System.Console.WriteLine("end: " + dtEnd.ToString("yyyy-MM-dd HH:mm:ss"));
TimeSpan s = dtEnd.Subtract(dtStart);
System.Console.WriteLine("diff: {0:00}:{1:00}", s.Minutes, s.Seconds);
}
static void readTest()
{
XmlTextReader reader = new XmlTextReader(sourcepath);
int cnt = 0;
while (reader.Read())
{
if (XmlNodeType.Element == reader.NodeType)
{
if (0 == ++cnt % 500000)
{
System.Console.Write('.');
}
}
}
System.Console.WriteLine("\n#elements: " + cnt + "\n");
}
static void Main()
{
FileInfo f = new FileInfo(sourcepath);
System.Console.WriteLine("filesize: {0:N0}", f.Length);
timing(readTest);
return;
}
}
}