解析PPTX 遇到异常:“\b”(十六进制值 0x08)是无效的字符。
阅读原文时间:2023年08月09日阅读:3

通过DocumentFormat.OpenXml解析PPTX文件时遇到异常:“\b”(十六进制值 0x08)是无效的字符,查看文件发现存在乱码,乱码的十六进制值刚好时异常中提到的0x08


网上有很多关于这类xml遇到无效字符异常的文章,其原因是xml中包含了不可打印的控制字符,解决办法是正则匹配替换这类字符。正则匹配的代码如下:

string r = "[\x00-\x08\x0B\x0C\x0E-\x1F\x26]";
return Regex.Replace(brokenXml, r, "", RegexOptions.Compiled);

原因和处理方式都有了,那么问题来了,加载PPTX文件的时候就抛出异常了,在什么时候替换xml中的控制字符呢?想起OpenXmlPowerTools的源码中关于处理文档中包含不合法的uri的代码,于是如法炮制,在加载页面slide的时候捕获XmlException类型的异常,在异常中修复xml内容

try
{
    .......
}
catch (XmlException xe)
{
    using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
    {
        XmlFixer.FixInvalidXml(fs, brokenXml =>
        {
            string r = "[\x00-\x08\x0B\x0C\x0E-\x1F\x26]";
            return Regex.Replace(brokenXml, r, "", RegexOptions.Compiled);
        });
    }
    return ReadPPTXText(filePath);
}

    public static class XmlFixer
    {
        public static void FixInvalidXml(Stream fs,Func invalidXmlHandler)
        {
            using (ZipArchive za = new ZipArchive(fs, ZipArchiveMode.Update))
            {
                bool IsInvalidXml = false;
                for (int i=0;i< za.Entries.Count;i++)
                {
                    var entry = za.Entries[i];
                    if (!entry.Name.EndsWith(".xml"))
                        continue;
                    bool replaceEntry = false;
                    XDocument entryXDoc = null;
                    using (var entryStream = entry.Open())
                    {
                        try
                        {
                            if (IsInvalidXml)
                            {
                                string content;
                                using (StreamReader sr = new StreamReader(entryStream))
                                {
                                    content = invalidXmlHandler(sr.ReadToEnd());
                                }
                                entryXDoc = XDocument.Parse(content);
                                IsInvalidXml = false;
                                replaceEntry = true;
                            }
                            else
                            {
                                entryXDoc = XDocument.Load(entryStream);
                                IsInvalidXml = false;
                            }
                        }
                        catch (XmlException xex)
                        {
                            i--;
                            IsInvalidXml = true;
                        }
                    }
                    if (replaceEntry)
                    {
                        var fullName = entry.FullName;
                        entry.Delete();
                        var newEntry = za.CreateEntry(fullName);
                        using (StreamWriter writer = new StreamWriter(newEntry.Open()))
                        using (XmlWriter xmlWriter = XmlWriter.Create(writer))
                        {
                            entryXDoc.WriteTo(xmlWriter);
                        }
                    }
                }
            }
        }
    }