You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在两类XML文件中替换游戏标题?XMLReader只读错误求解

解决大型XML游戏数据文件的标题批量替换问题

问题背景

你有两类存储复古游戏信息的XML文件,需要针对性修改游戏标题:

  1. LaunchBox XML文件:替换<Title>Star Strike</Title>为<Title>Star Wars</Title>
  2. .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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 21:00:37