什么会导致 XML 文件被空字符填充?
What could cause an XML file to be filled with null characters?
这是一个棘手的问题。我怀疑这需要一些文件系统的高级知识才能回答。
我有一个 WPF 应用程序,"App1," 针对 .NET Framework 4.0。它有一个 Settings.settings
文件,可生成一个标准 App1.exe.config
文件,其中存储默认设置。当用户修改设置时,修改将进入 AppData\Roaming\MyCompany\App1\X.X.0.0\user.config
。这都是标准的 .NET 行为。然而,有时,我们发现客户机器上的 user.config
文件不是它应该的样子,这会导致应用程序崩溃。
问题看起来是这样的:user.config
大约是如果用 XML 填充它应该有的大小,但不是 XML 它只是一堆 NUL 字符。它是字符 0 一遍又一遍地重复。我们没有关于导致此文件修改的信息。
如果我们只删除 user.config
,我们就可以在客户的设备上解决这个问题,因为公共语言运行时只会生成一个新的。他们将丢失对设置所做的更改,但可以再次进行更改。
但是,我在另一个 WPF 应用程序 "App2," 和另一个 XML 文件 info.xml
中遇到了这个问题。这次有所不同,因为文件是由我自己的代码生成的,而不是由 CLR 生成的。共同的主题是两者都是 C# WPF 应用程序,都是 XML 文件,在这两种情况下我们都完全无法在我们的测试中重现问题。这可能与 C# 应用程序与 XML 文件或一般文件交互的方式有关吗?
不仅不能在我们当前的应用程序中重现该问题,而且我什至无法通过编写故意生成错误的自定义代码来重现该问题。我找不到导致文件充满空值的单个 XML 序列化错误或文件访问错误。那么会发生什么?
App1 通过调用 Upgrade()
和 Save()
以及获取和设置属性来访问 user.config
。例如:
if (Settings.Default.UpgradeRequired)
{
Settings.Default.Upgrade();
Settings.Default.UpgradeRequired = false;
Settings.Default.Save();
}
App2 通过序列化和反序列化 XML:
来访问 info.xml
public Info Deserialize(string xmlFile)
{
if (File.Exists(xmlFile) == false)
{
return null;
}
XmlSerializer xmlReadSerializer = new XmlSerializer(typeof(Info));
Info overview = null;
using (StreamReader file = new StreamReader(xmlFile))
{
overview = (Info)xmlReadSerializer.Deserialize(file);
file.Close();
}
return overview;
}
public void Serialize(Info infoObject, string fileName)
{
XmlSerializer writer = new XmlSerializer(typeof(Info));
using (StreamWriter fileWrite = new StreamWriter(fileName))
{
writer.Serialize(fileWrite, infoObject);
fileWrite.Close();
}
}
我们在 Windows 7 和 Windows 10 上都遇到了这个问题。在研究这个问题时,我遇到了这个 post,其中有相同的 XML 问题在Windows8.1中遇到:Saved files sometime only contains NUL-characters
我是否可以更改我的代码以防止出现这种情况,或者问题是否在 .NET 的行为中太深了?
我觉得有3种可能:
- CLR 正在将空字符写入 XML 文件。
- 文件的内存地址指针在不移动文件内容的情况下切换到另一个位置。
- 文件系统试图将文件移动到另一个内存地址,文件内容被移动但指针没有更新。
我觉得 2 和 3 比 1 更有可能。这就是为什么我说它可能需要高级文件系统知识。
如果有任何信息可以帮助我重现、修复或解决问题,我将不胜感激。谢谢!
这种行为没有记录在案的原因,因为这种情况发生在用户身上,但没有人能说出这种奇怪情况的起源。
这可能是 CLR 问题,虽然这不太可能,但 CLR 不会只写入空字符,如果没有为节点定义 xsi:nil,XML 文档不能包含空字符.
无论如何,解决此问题的唯一记录方法是使用以下代码行删除损坏的文件:
try
{
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
}
catch (ConfigurationErrorsException ex)
{
string filename = ex.Filename;
_logger.Error(ex, "Cannot open config file");
if (File.Exists(filename) == true)
{
_logger.Error("Config file {0} content:\n{1}", filename, File.ReadAllText(filename));
File.Delete(filename);
_logger.Error("Config file deleted");
Properties.Settings.Default.Upgrade();
// Properties.Settings.Default.Reload();
// you could optionally restart the app instead
}
else
{
_logger.Error("Config file {0} does not exist", filename);
}
}
它将使用 Properties.Settings.Default.Upgrade();
恢复 user.config
再次没有空值。
我遇到了类似的问题,我能够将我的问题追溯到损坏的 HDD。
我的问题描述(所有相关信息):
连接到主板的磁盘 (SATA):
SSD (系统),
3 * 硬盘。
其中一个硬盘有坏块,甚至读取磁盘结构也有问题(目录和文件列表)。
操作系统:Windows7 x64
文件系统(在所有磁盘上):NTFS
当系统尝试读取或写入损坏的磁盘(用户请求或自动扫描或任何其他原因)并且尝试失败时,所有写入操作(to other disk's) 不正确。在系统磁盘上创建的文件(主要是其他应用程序的配置文件)已写入并且有效(可能是因为文件在RAM中兑现)直接检查文件内容。
不幸的是,重新启动后,所有文件(在 write/read 访问失败后写入损坏的驱动器) 具有正确的大小,但文件的内容是 'zero byte' (与您的情况完全一样).
尝试排除与硬件相关的问题。您可以尝试检查 'copy' 文件(更改后)到另一台机器 (上传到 web/ftp)。或者尝试将特定内容保存到固定文件中。当不同的检查文件正确时,或者当固定内容文件'empty'时,原因可能是在本地机器上。尝试更改硬件组件,或重装系统。
我 运行 遇到了类似的问题,但它是在服务器上。服务器在程序写入文件时重新启动,这导致该文件包含所有空字符并且程序无法使用该文件 writing/reading。
所以文件看起来像这样:
日志显示服务器重新启动:
损坏的文件显示它的最后更新时间是重启时:
众所周知,如果断电,就会发生这种情况。这发生在扩展文件(它可以是新文件或现有文件)的缓存写入之后,并且此后不久发生断电。在这种情况下,当机器恢复时文件有 3 种预期的可能状态:
1) 文件根本不存在或具有原始长度,就好像写入从未发生过。
2) 文件具有预期的长度,就像发生了写入一样,但数据为零。
3) 文件具有预期的长度和写入的正确数据。
状态 2 就是您所描述的。发生这种情况是因为当您执行缓存写入时,NTFS 最初只是相应地扩展文件大小但保持 VDL(有效数据长度)不变。 VDL 之外的数据始终读回为零。您打算写入的数据位于文件缓存的内存中。它最终将被写入磁盘,通常在几秒钟内,然后 VDL 将在磁盘上前进以反映写入的数据。如果在写入数据之前或 VDL 增加之前发生断电,您将进入状态 2。
这很容易重现,例如复制文件(复制引擎使用缓存写入),然后立即拔下计算机的电源插头。
我有同样的问题,序列化后的xml文件末尾多了一个"NUL"字符:
我是这样使用 XMLWriter 的:
using (var stringWriter = new Utf8StringWriter())
{
using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true, IndentChars = "\t", NewLineChars = "\r\n", NewLineHandling = NewLineHandling.Replace }))
{
xmlSerializer.Serialize(xmlWriter, data, nameSpaces);
xml = stringWriter.ToString();
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
if (removeEmptyNodes)
{
RemoveEmptyNodes(xmlDocument);
}
xml = xmlDocument.InnerXml;
}
}
这是一个棘手的问题。我怀疑这需要一些文件系统的高级知识才能回答。
我有一个 WPF 应用程序,"App1," 针对 .NET Framework 4.0。它有一个 Settings.settings
文件,可生成一个标准 App1.exe.config
文件,其中存储默认设置。当用户修改设置时,修改将进入 AppData\Roaming\MyCompany\App1\X.X.0.0\user.config
。这都是标准的 .NET 行为。然而,有时,我们发现客户机器上的 user.config
文件不是它应该的样子,这会导致应用程序崩溃。
问题看起来是这样的:user.config
大约是如果用 XML 填充它应该有的大小,但不是 XML 它只是一堆 NUL 字符。它是字符 0 一遍又一遍地重复。我们没有关于导致此文件修改的信息。
如果我们只删除 user.config
,我们就可以在客户的设备上解决这个问题,因为公共语言运行时只会生成一个新的。他们将丢失对设置所做的更改,但可以再次进行更改。
但是,我在另一个 WPF 应用程序 "App2," 和另一个 XML 文件 info.xml
中遇到了这个问题。这次有所不同,因为文件是由我自己的代码生成的,而不是由 CLR 生成的。共同的主题是两者都是 C# WPF 应用程序,都是 XML 文件,在这两种情况下我们都完全无法在我们的测试中重现问题。这可能与 C# 应用程序与 XML 文件或一般文件交互的方式有关吗?
不仅不能在我们当前的应用程序中重现该问题,而且我什至无法通过编写故意生成错误的自定义代码来重现该问题。我找不到导致文件充满空值的单个 XML 序列化错误或文件访问错误。那么会发生什么?
App1 通过调用 Upgrade()
和 Save()
以及获取和设置属性来访问 user.config
。例如:
if (Settings.Default.UpgradeRequired)
{
Settings.Default.Upgrade();
Settings.Default.UpgradeRequired = false;
Settings.Default.Save();
}
App2 通过序列化和反序列化 XML:
来访问info.xml
public Info Deserialize(string xmlFile)
{
if (File.Exists(xmlFile) == false)
{
return null;
}
XmlSerializer xmlReadSerializer = new XmlSerializer(typeof(Info));
Info overview = null;
using (StreamReader file = new StreamReader(xmlFile))
{
overview = (Info)xmlReadSerializer.Deserialize(file);
file.Close();
}
return overview;
}
public void Serialize(Info infoObject, string fileName)
{
XmlSerializer writer = new XmlSerializer(typeof(Info));
using (StreamWriter fileWrite = new StreamWriter(fileName))
{
writer.Serialize(fileWrite, infoObject);
fileWrite.Close();
}
}
我们在 Windows 7 和 Windows 10 上都遇到了这个问题。在研究这个问题时,我遇到了这个 post,其中有相同的 XML 问题在Windows8.1中遇到:Saved files sometime only contains NUL-characters
我是否可以更改我的代码以防止出现这种情况,或者问题是否在 .NET 的行为中太深了?
我觉得有3种可能:
- CLR 正在将空字符写入 XML 文件。
- 文件的内存地址指针在不移动文件内容的情况下切换到另一个位置。
- 文件系统试图将文件移动到另一个内存地址,文件内容被移动但指针没有更新。
我觉得 2 和 3 比 1 更有可能。这就是为什么我说它可能需要高级文件系统知识。
如果有任何信息可以帮助我重现、修复或解决问题,我将不胜感激。谢谢!
这种行为没有记录在案的原因,因为这种情况发生在用户身上,但没有人能说出这种奇怪情况的起源。
这可能是 CLR 问题,虽然这不太可能,但 CLR 不会只写入空字符,如果没有为节点定义 xsi:nil,XML 文档不能包含空字符.
无论如何,解决此问题的唯一记录方法是使用以下代码行删除损坏的文件:
try
{
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
}
catch (ConfigurationErrorsException ex)
{
string filename = ex.Filename;
_logger.Error(ex, "Cannot open config file");
if (File.Exists(filename) == true)
{
_logger.Error("Config file {0} content:\n{1}", filename, File.ReadAllText(filename));
File.Delete(filename);
_logger.Error("Config file deleted");
Properties.Settings.Default.Upgrade();
// Properties.Settings.Default.Reload();
// you could optionally restart the app instead
}
else
{
_logger.Error("Config file {0} does not exist", filename);
}
}
它将使用 Properties.Settings.Default.Upgrade();
恢复 user.config
再次没有空值。
我遇到了类似的问题,我能够将我的问题追溯到损坏的 HDD。
我的问题描述(所有相关信息):
连接到主板的磁盘 (SATA):
SSD (系统),
3 * 硬盘。
其中一个硬盘有坏块,甚至读取磁盘结构也有问题(目录和文件列表)。
操作系统:Windows7 x64
文件系统(在所有磁盘上):NTFS
当系统尝试读取或写入损坏的磁盘(用户请求或自动扫描或任何其他原因)并且尝试失败时,所有写入操作(to other disk's) 不正确。在系统磁盘上创建的文件(主要是其他应用程序的配置文件)已写入并且有效(可能是因为文件在RAM中兑现)直接检查文件内容。
不幸的是,重新启动后,所有文件(在 write/read 访问失败后写入损坏的驱动器) 具有正确的大小,但文件的内容是 'zero byte' (与您的情况完全一样).
尝试排除与硬件相关的问题。您可以尝试检查 'copy' 文件(更改后)到另一台机器 (上传到 web/ftp)。或者尝试将特定内容保存到固定文件中。当不同的检查文件正确时,或者当固定内容文件'empty'时,原因可能是在本地机器上。尝试更改硬件组件,或重装系统。
我 运行 遇到了类似的问题,但它是在服务器上。服务器在程序写入文件时重新启动,这导致该文件包含所有空字符并且程序无法使用该文件 writing/reading。
所以文件看起来像这样:
日志显示服务器重新启动:
损坏的文件显示它的最后更新时间是重启时:
众所周知,如果断电,就会发生这种情况。这发生在扩展文件(它可以是新文件或现有文件)的缓存写入之后,并且此后不久发生断电。在这种情况下,当机器恢复时文件有 3 种预期的可能状态:
1) 文件根本不存在或具有原始长度,就好像写入从未发生过。
2) 文件具有预期的长度,就像发生了写入一样,但数据为零。
3) 文件具有预期的长度和写入的正确数据。
状态 2 就是您所描述的。发生这种情况是因为当您执行缓存写入时,NTFS 最初只是相应地扩展文件大小但保持 VDL(有效数据长度)不变。 VDL 之外的数据始终读回为零。您打算写入的数据位于文件缓存的内存中。它最终将被写入磁盘,通常在几秒钟内,然后 VDL 将在磁盘上前进以反映写入的数据。如果在写入数据之前或 VDL 增加之前发生断电,您将进入状态 2。
这很容易重现,例如复制文件(复制引擎使用缓存写入),然后立即拔下计算机的电源插头。
我有同样的问题,序列化后的xml文件末尾多了一个"NUL"字符:
我是这样使用 XMLWriter 的:
using (var stringWriter = new Utf8StringWriter())
{
using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true, IndentChars = "\t", NewLineChars = "\r\n", NewLineHandling = NewLineHandling.Replace }))
{
xmlSerializer.Serialize(xmlWriter, data, nameSpaces);
xml = stringWriter.ToString();
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
if (removeEmptyNodes)
{
RemoveEmptyNodes(xmlDocument);
}
xml = xmlDocument.InnerXml;
}
}