解决 Excel 单元格显示与复制不一样的情况
简介
收到一份 Excel 文件,表面看起来一切正常,但是表格内所有数据单元格显示与表格上方编辑栏内容不符,复制单元格到记事本,显示内容与编辑栏一致,看起来是有人刻意对单元格文本进行加盐处理,应该是为了防止拷贝直接使用数据。
参考
帖子 excel单元格怎么让实际值与显示的值一致 与 电子表格里的数据如何去除隐藏部分 中有遇到相同情况,当中有人给出了原理,但是并没有提供一种快速的解决办法。
验证
拷贝一个单元格到记事本中,可以看到显示内容与 Excel 编辑中一致为加盐后的结果。
拷贝一个单元格数据到 Word 中,可以看到显示内容正确,但是仔细看左下角开头部位有几个字号为1磅或2磅不易察觉的宽度。
解决办法
处理前准备
- 前文参考的帖子中有提到替换的方式,我尝试在 Excel 中按照字号替换,最终失败,一格一格数据拷贝到 Word 中替换,这个数据量着实劝退。
- 使用 NPOI 读取单元格格式,替换其中的文本,但是在长时候发现 NPOI 对单元格中多种字体大小颜色很难判断,最终放弃。
- Excel 实际为压缩文件固定格式,可以解压缩获取其中 XML 文件,从而修改,解压缩后看到单元格数据都储存在 “\xl\sharedStrings.xml” 文件中,但是不知为何,这个 XML 文件的节点并不完整,解析时会报错,修复文件是个不小的工作量,并且格式恢复为 .xlsx 后 Excel 报文件损坏,所以放弃。
- 通过另存的方式保存为 XML 格式后再做解析,另存为 “XML 数据”,提示错误:”工作簿不包含任何 XML 映射”,所以另存为 “XML 电子表格 2003”,可以看到每个单元格内的文字格式。
1 2 3 4 5 6 7 8
| <Cell ss:StyleID="s68"> <ss:Data ss:Type="String" xmlns="http://www.w3.org/TR/REC-html40"> <Font html:Color="#FFFFF2">a1</Font> <Font html:Size="9">13763569</Font> <Font html:Size="1" html:Color="#FFFFCC">8</Font> <Font html:Size="9">999</Font> </ss:Data> </Cell>
|
判断加盐内容
多找几条数据后发现加盐的干扰项为(字体字号为 1磅 或 2磅,颜色为不易察觉的白色):
1 2 3 4 5 6
| html:Color="#FFFFF2" html:Color="#FFFFF1" html:Color="#FFFFCC" html:Color="#FFFFFF" html:Size="1" html:Size="2"
|
处理文件代码
完整代码:ExcelCellDecryption
程序运行选择另存为 “XML 电子表格 2003” 的 XML 文件,点击执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| XmlDocument doc = new XmlDocument(); doc.Load(vNewFile); XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("ab", "http://www.w3.org/TR/REC-html40"); nsmgr.AddNamespace("ss", "urn:schemas-microsoft-com:office:spreadsheet");
XmlNodeList nodeFonts = doc.SelectNodes("//ab:Font", nsmgr); for (int i = 0; i < nodeFonts.Count; i++) { var vXmlNodeFont = nodeFonts[i]; bool bIsRemove = false; foreach (var item in listRemoveIdentification) { if (vXmlNodeFont.OuterXml.Contains(item)) { bIsRemove = true; break; } } if (bIsRemove) { var vParentNode = vXmlNodeFont.ParentNode; vParentNode.RemoveChild(vXmlNodeFont); } }
XmlNodeList nodeDatas = doc.SelectNodes("//ss:Data", nsmgr); for (int i = 0; i < nodeDatas.Count; i++) { var vXmlNodeData = nodeDatas[i]; var vXmlNodeFonts = vXmlNodeData.ChildNodes; if (vXmlNodeFonts.Count >= 2) { string strTxt = ""; XmlNode xmlNodeMain = null; List<XmlNode> xmlNodesPrepare = new List<XmlNode>(); for (int j = 0; j < vXmlNodeFonts.Count; j++) { var vXmlNodeFont = vXmlNodeFonts[j]; if (j == 0) { xmlNodeMain = vXmlNodeFont; } else { xmlNodesPrepare.Add(vXmlNodeFont); } strTxt += vXmlNodeFont.InnerText; } if (strTxt.Length >= 15 && IsNumeric(strTxt) && !strTxt.First().Equals('\'')) { xmlNodeMain.InnerText = $"'{strTxt}"; } else { xmlNodeMain.InnerText = strTxt; } var vParentNode = xmlNodeMain.ParentNode; for (int k = 0; k < xmlNodesPrepare.Count; k++) { vParentNode.RemoveChild(xmlNodesPrepare[k]); } } } doc.Save(vNewFile);
|
手动处理
- 执行完成后会生成:”{XXX}_decrypt.xml” 文件,使用 Excel 打开。
- 修改整表字号为标准大小(我这里是宋体9号)。
- Ctrl+H 打开查找和替换,替换所有 “ “ 为 “”。
- 另存文件为 .xlsx 格式,处理结束。
如何制作这样的数据
既然解决了问题,那么在按照原路制造出来也是比较简单的,比如直接修改 xml 文件在 Cell 单元格中添加不易察觉的 Font,或者参考帖子 NPOI Excel同一个单元格 多种字体 使用 NPOI 写入即可。