给出两个XML文件,其内容开始如下:
文件A
<?xml
文件B
<?xml
文件B将错误。因此,我们实施了以下代码来删除BOM,后者已在生产环境中使用了多年:
private static string RemoveUTF8ByteOrderMark(string str)
{
var byteOrderMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
if (str.StartsWith(byteOrderMarkUtf8))
{
str = str.Remove(0, byteOrderMarkUtf8.Length);
}
return str;
}
一旦删除BOM,我们将使用以下命令将字符串解析为XML:
public static XDocument ParseXmlDocumentFromText(string fileText)
{
if (string.IsNullOrEmpty(fileText)) return null;
var nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("*****", "*****");
var ctx = new XmlParserContext(null, nsm, null, XmlSpace.Default);
var settings = new XmlReaderSettings { ProhibitDtd = false, XmlResolver = null };
using (var fs = new StringReader(fileText))
{
using (var reader = XmlReader.Create(fs, settings, ctx))
{
var doc = new XmlDocument();
doc.Load(reader);
return XDocument.Parse(doc.OuterXml);
}
}
}
现在,我们已经将网站从专用服务器迁移到Azure Web App,并且在文件B继续正确加载的同时,文件A在doc.Load(reader);
处出错。
System.Web.HttpUnhandledException(0x80004005):类型的异常 引发了“ System.Web.HttpUnhandledException”。 -> System.Xml.XmlException:根级别的数据无效。 1号线 位置1。
在Azure中,如果我禁用了RemoveUTF8ByteOrderMark(...)
调用,则文件A正确加载,并且文件B错误(按预期)。
当我在计算机上进行本地测试时,两个文件都启用了RemoveUTF8ByteOrderMark(...)
加载,这与我们的旧专用服务器一致。在所有3种环境中,XML文件都是从Azure blob存储中提取的,因此始终使用完全相同的文件。
Azure Web App中正在发生什么,正在更改此代码的运行方式?
更新
在Azure中,调用RemoveUTF8ByteOrderMark(...)
时,我看到返回的文本如下:
文件A
?xml
文件B
<?xml
那为什么RemoveUTF8ByteOrderMark(...)
显然导致开头的<
被剥夺了?
答案 0 :(得分:1)
看来,您的代码对于使用UTF-8编码处理文件没有问题。但是,根据Wiki页面Byte order mark
,文件中BOM表头的字节长度不同,编码方式也不同,如下图所示。
因此,一种用于删除通用文件BOM头的解决方案,您需要首先检测文件编码,然后删除文件头的不同长度的字节以获取没有BOM的真实内容。
C#和C ++中有一个GitHub存储库AutoItConsulting/text-encoding-detect
,可帮助检测文本内容编码并检查BOM,如下the code所示。
/// <summary>
/// Gets the BOM length for a given Encoding mode.
/// </summary>
/// <param name="encoding"></param>
/// <returns>The BOM length.</returns>
public static int GetBomLengthFromEncodingMode(Encoding encoding)
{
int length;
switch (encoding)
{
case Encoding.Utf16BeBom:
case Encoding.Utf16LeBom:
length = 2;
break;
case Encoding.Utf8Bom:
length = 3;
break;
default:
length = 0;
break;
}
return length;
}
/// <summary>
/// Checks for a BOM sequence in a byte buffer.
/// </summary>
/// <param name="buffer"></param>
/// <param name="size"></param>
/// <returns>Encoding type or Encoding.None if no BOM.</returns>
public Encoding CheckBom(byte[] buffer, int size)
{
// Check for BOM
if (size >= 2 && buffer[0] == _utf16LeBom[0] && buffer[1] == _utf16LeBom[1])
{
return Encoding.Utf16LeBom;
}
if (size >= 2 && buffer[0] == _utf16BeBom[0] && buffer[1] == _utf16BeBom[1])
{
return Encoding.Utf16BeBom;
}
if (size >= 3 && buffer[0] == _utf8Bom[0] && buffer[1] == _utf8Bom[1] && buffer[2] == _utf8Bom[2])
{
return Encoding.Utf8Bom;
}
return Encoding.None;
}
我认为您可以直接使用或更改这些代码来解决问题,以删除文件中可能的BOM字节。
同时,如果您只需要处理XML文件的内容,我认为一种简单的方法是将IndexOf
与<?xml
一起使用具有可能的BOM字节的字符串,因为<?xml
字符串是XML DTD中的固定内容。
这是我的示例代码,它适用于带有或不带有BOM字节的xml文件。
int index = str.IndexOf("<?xml");
return str.Substring(index);
希望有帮助。