如何在两类XML文件中替换游戏标题?XMLReader只读错误求解
解决大型XML游戏数据文件的标题批量替换问题
问题背景
你有两类存储复古游戏信息的XML文件,需要针对性修改游戏标题:
- LaunchBox XML文件:替换
<Title>Star Strike</Title>为<Title>Star Wars</Title> - .dat格式XML文件:替换
<game name="KeepAway (USA)">为<game name="KeepAway 2 (USA)">
文件最大包含10万个游戏条目,无法直接加载整个文件到内存,尝试用XMLReader时出现Cannot write to read-only property错误——核心原因是XMLReader是只读解析器,无法直接修改节点值,需要配合XMLWriter实现流式读写,避免内存溢出。
解决方案:流式XML处理(XMLReader + XMLWriter)
流式处理仅在内存中保留当前解析的节点,完美适配超大文件场景,以下是两类文件的针对性实现:
1. 处理LaunchBox XML文件
<?php $inputFile = 'path/to/your/launchbox.xml'; $outputFile = 'path/to/your/modified_launchbox.xml'; $searchTitle = 'Star Strike'; $replaceTitle = 'Star Wars'; $reader = new XMLReader(); $writer = new XMLWriter(); $reader->open($inputFile); $writer->openURI($outputFile); $writer->setIndent(true); $writer->startDocument('1.0', 'UTF-8', 'yes'); // 流式遍历所有节点 while ($reader->read()) { switch ($reader->nodeType) { case XMLReader::ELEMENT: $writer->startElement($reader->name); // 复制当前节点的所有属性 if ($reader->hasAttributes) { while ($reader->moveToNextAttribute()) { $writer->writeAttribute($reader->name, $reader->value); } } // 如果当前是Title标签,检查是否需要替换内容 if ($reader->name === 'Title') { $reader->read(); // 移动到文本节点 $currentValue = $reader->value; $writer->text($currentValue === $searchTitle ? $replaceTitle : $currentValue); $reader->next(); // 跳过结束标签 $writer->endElement(); } break; case XMLReader::END_ELEMENT: $writer->endElement(); break; case XMLReader::TEXT: case XMLReader::CDATA: $writer->text($reader->value); break; case XMLReader::COMMENT: $writer->writeComment($reader->value); break; case XMLReader::PI: $writer->writePI($reader->name, $reader->value); break; } } $writer->endDocument(); $reader->close(); $writer->flush(); // 可选:测试无误后替换原文件 // rename($outputFile, $inputFile); ?>
2. 处理.dat格式XML文件
<?php $inputFile = 'path/to/your/games.dat'; $outputFile = 'path/to/your/modified_games.dat'; $searchName = 'KeepAway (USA)'; $replaceName = 'KeepAway 2 (USA)'; $reader = new XMLReader(); $writer = new XMLWriter(); $reader->open($inputFile); $writer->openURI($outputFile); $writer->setIndent(true); $writer->startDocument('1.0', 'UTF-8'); // 保留原文件的DOCTYPE声明 if ($reader->nodeType === XMLReader::DOC_TYPE) { $writer->writeDtd( $reader->name, $reader->publicId, $reader->systemId ); $reader->read(); } // 流式遍历所有节点 while ($reader->read()) { switch ($reader->nodeType) { case XMLReader::ELEMENT: $writer->startElement($reader->name); // 复制当前节点的所有属性 if ($reader->hasAttributes) { while ($reader->moveToNextAttribute()) { $attrValue = $reader->value; // 如果是game标签的name属性,检查是否需要替换 if ($reader->name === 'name' && $reader->localName === 'game') { $attrValue = $attrValue === $searchName ? $replaceName : $attrValue; } $writer->writeAttribute($reader->name, $attrValue); } } // 处理空元素 if ($reader->isEmptyElement) { $writer->endElement(); } break; case XMLReader::END_ELEMENT: $writer->endElement(); break; case XMLReader::TEXT: case XMLReader::CDATA: $writer->text($reader->value); break; case XMLReader::COMMENT: $writer->writeComment($reader->value); break; case XMLReader::PI: $writer->writePI($reader->name, $reader->value); break; } } $writer->endDocument(); $reader->close(); $writer->flush(); // 可选:测试无误后替换原文件 // rename($outputFile, $inputFile); ?>
关键说明
- 内存友好:仅在内存中处理当前节点,10万条数据也不会出现内存溢出
- 格式保留:完整保留原文件的编码、缩进、DOCTYPE等结构信息
- 安全操作:先输出到新文件,测试无误后再替换原文件,避免数据丢失
内容的提问来源于stack exchange,提问作者John T
相关产品推荐
相关产品推荐

