Excel歪门邪道(1) — 用C#解决Excel修复文件后批注被删除的问题

news/2024/9/19 18:39:02/文章来源:https://www.cnblogs.com/FEAGLESTUDIO/p/18418362

今天是2024年9月18日,中秋节刚过,狠下心来决定把原来发在CSDN的文章全部搬过来,倒不是因为文章被CSDN拿去白嫖或者是担心CSDN倒闭,而是觉得自己过去的29年生活自己活的像个小丑,如今已到而立之年被社会彻底淘汰已成定局,因此在离开这个世界之前,我还是决定把过往的一些没什么用的经验重新用文字记录在博客园,哪天我真的死了,我起码在园子发过一点稍微有用的东西,至于有多少用,就很难讲了,毕竟有了AI以后,AI势必会接管一切,但是学习知识的目的却从未变过,因为当你有互联网时你能用AI,等到你成单机了的时候,你连百度都用不了了,一些很简单的问题你也束手无策的时候,那才是AI战胜人类真正完成闭环。

本篇是Excel歪门邪道第一篇,问题要追溯到我第一份工作时遇到的问题,当然,文章本身并无意义,因为现在普遍用WPS的情况下,批注的问题已经被WPS消灭了,本文探讨的是问题出现的原因以及用Winform C#程序完成该操作的思路

首先批注被删的表现形式:其一是常见于Excel2007打开别人发的Excel工作簿文件时,显示文件有错误,修复文件后显示已删除批注,然后别人写的批注全都没了;其二是xlsx文件都有该问题,但是xls文件安然无恙

这里就要提到两个问题:第一为什么批注会被删,第二为什么xls文件不会有这个报错

批注被删的主要原因是Excel2007无法识别批注,具体点就是Excel2007程序无法正常识别之后版本的一些表格外的对象(object)包括但不限于:批注、图形、数据验证等等,这和微软OpenXML标准也有关系,从Excel2007以后,微软开始用了基于OpenXML的一中新的文件格式:xlsx,同样的Word的叫docx,PowerPoint叫pptx,三者其实都是基于一个技术的产物,然而微软在迭代Office用的OpenXML标准的时候并没有向后兼容的问题,这就为早期版本的Office套件不兼容标准埋下了祸根。

按照Excelhome论坛用户jaffedream在论坛回帖中的说法,Excel2007开始识别批注异常的问题要追溯到批注在XML文件中的记录方式,在Excel2007以后的版本中,comments*.xml文件中对批注记录多了一个ShapeId="0"的节点,这就导致Excel2007等版本中,解析器在解析XML文件时无法识别到这个节点,因此会提示用户文件有错误,询问是否修复。但是这里肯定是不能点确定去修复的,因为一旦让Excel修复,由于Excel还是不认识这个节点,最后为了文件能被正常读取,还是会直接删掉整个批注在的项,等于没有修复,关键的信息还是丢失了。

至于xls文件为什么不会有这个报错,因为xls本来就是为了兼容97-2003期间的标准,这个标准已经定型不会发生任何变化了,也因为这个原因,xls兼容性很强,很难出现文件异常的问题,但这并不代表xls就是万用解,首先xls文件不支持后续的一些功能,例如切片器、PQ编辑器等,其次是表格范围,xls的列最大只能支持到256列,行最大只能支持到65536行,而xlsx的这两项分别最大支持16384列和1048576行,这也是为什么xls文件中如果引用xlsx文件的列直接用类似$A:$A之类的方法引用时,会提示无效引用,该文件版本包含的公式中不能引用范围外区域的问题,然后是宏的问题,xlsx默认是不允许宏(marco)存在的,如果要允许使用宏必须另存为xlsm文件,而xls是允许嵌入宏代码的,而这并不安全,因为如果你的电脑默认是允许宏启动的(虽然微软知道宏不安全默认给关了),如果xls被加入了恶意的宏代码,那么随着文件打开就会自动运行宏病毒,但你说xlsx不允许宏存在是不是就万事大吉了呢?未必——因为CSDN上的有人已经发现了瞒天过海的方法,这个有机会再说。

所以讲了一大通废话,关键要修复这个问题就几步(甚至可以自己手动改过来):

1.解压缩文件结构,把comments文件解压出来

2.删除comments文件中所有的ShapeId="0"

3.把修改过的comments文件重新打回压缩包(至于为什么不能新建一个压缩包把所有文件直接打进去,因为Excel工作簿和压缩包打包方式不一样,这样做Excel程序识别不了)

 

下面是实现的代码,解压缩模块用到了SharpZip

  1 using System;
  2 using System.Text;
  3 using System.Windows.Forms;
  4 using System.IO.Compression;
  5 using System.IO;
  6 using CZip = ICSharpCode.SharpZipLib.Zip;
  7  
  8 private void Button1_Click(object sender, EventArgs e)
  9         {
 10             int i = 0;
 11             OpenFileDialog path = new OpenFileDialog();
 12             //文件类型过滤
 13             path.Filter = @"待修复Excel表格|*.xlsx";
 14             //展开文件选择对话框
 15             DialogResult result = path.ShowDialog();
 16             if (result == DialogResult.OK)
 17             {
 18                 //获取导出文件夹目录
 19                 string expath = path.FileName + @"_Fix";
 20               if(Directory.Exists(expath))
 21                 {
 22                     DialogResult fg= MessageBox.Show("导出数据已存在!是否覆盖?覆盖后将失去所有已导出数据!", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
 23                     if (fg == DialogResult.Yes)//确认删除
 24                     {
 25                         //对重复文件夹进行删除操作
 26                         DirectoryInfo dl = new DirectoryInfo(expath);
 27                         dl.Delete(true);
 28                         DialogResult qr= MessageBox.Show("删除命令已执行,是否继续?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
 29                         if (qr == DialogResult.Yes)
 30                             {
 31                                 //继续执行命令
 32                             }
 33                             else
 34                             {
 35                                 if (qr == DialogResult.No)//删除文件夹后不继续
 36                                 {
 37                                     i = 1;
 38                                 }
 39                             }
 40                     }
 41                     else
 42                     {
 43                         if (fg == DialogResult.No)//确认不删除
 44                         {
 45                             i = 1;
 46                         }
 47                     }
 48                 }
 49                 //强制用户确认后才进行解压操作
 50                 if (i == 0)
 51                 {
 52                     //获取打开文件的路径(全路径,含文件名)
 53                     int fnlength = path.ToString().LastIndexOf("FileName");
 54                     string sFile = path.ToString().Substring(fnlength + 9, path.ToString().Length - fnlength - 9);
 55                     //获取打开文件所在目录,传递至sourcepath
 56                     string sourcepath = Path.GetDirectoryName(sFile);
 57  
 58                     //开始解压文件
 59                     ZipFile.ExtractToDirectory(path.FileName, expath);
 60                     //跳转至xl文件夹下
 61                     string copath = expath + "\\xl\\";
 62                     //获取xl文件夹下所有带有comments字符的xml文件路径
 63                     string[] pathFile = Directory.GetFiles(copath, "comments*.xml", SearchOption.TopDirectoryOnly);
 64                     //将中间文件命名为cobak.xml
 65                     string strcon = copath + @"cobak.xml";
 66                     //暂时将替换容器内容清空
 67                     string con = "";
 68                     foreach (string str in pathFile)
 69                     {
 70                         StreamReader reader = new StreamReader(str, Encoding.UTF8);
 71                         //读取至文件尾
 72                         con = reader.ReadToEnd();
 73                         //替换shapeId="0"为空值
 74                         con = con.Replace(@"shapeId=""0""", "");
 75                         //替换Excel2013的不正确的XML代码块
 76                         con = con.Replace(@"</commment></commentList></comments>", "");
 77  
 78                         //开始以UTF-8编码开始写操作
 79                         StreamWriter writer = new StreamWriter(strcon, false, Encoding.UTF8);
 80                         //写入已替换的内容
 81                         writer.Write(con);
 82                         //释放写入缓冲区,暂时关闭写入器
 83                         writer.Flush();
 84                         writer.Close();
 85                         reader.Close();
 86  
 87                         //将写入文件复制为原文件,删除写入文件
 88                         File.Copy(strcon, str, true);
 89                         File.Delete(strcon);
 90                     }
 91                     //到此替换写入操作已完成
 92  
 93                     //开始修改文件扩展名
 94                     string nFile = Path.ChangeExtension(sFile, ".zip");
 95                     FileInfo fi = new FileInfo(sFile);
 96                     fi.MoveTo(nFile);
 97  
 98                         //开始Czip操作
 99                         CZip.ZipFile zip = new CZip.ZipFile(nFile);
100                         zip.BeginUpdate();
101                         //获取每个comments.xml文件的目录
102                         foreach (string str in pathFile)
103                         {
104                             string filename = Path.GetFileName(str);
105                             zip.Add(str,@"/xl/" + filename);
106                             
107                         }
108                         zip.CommitUpdate();
109                     //关闭Czip
110                     zip.Close();
111                     //将文件扩展名改回xlsx
112                     string endFile =Path.ChangeExtension(nFile, ".xlsx");
113                     //删除临时目录
114                     fi.CopyTo(endFile, true);
115                     DirectoryInfo dl = new DirectoryInfo(expath);
116                     dl.Delete(true);
117                     //删除临时文件
118                     File.Delete(nFile);
119  
120  
121                     System.Diagnostics.Process.Start("explorer.exe", sourcepath);
122                 }
123             }

 

Github下载地址:

https://github.com/InfinityEx/Excel-Comments-Fixer/releases/tag/Debug

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/799325.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决ZBLOG网站运行中出现JavaScript加载失败问题

当 Z-Blog 网站运行中出现“JavaScript 加载失败”的问题时,可能的原因有几个方面,包括但不限于插件冲突、HTTPS 配置问题、CDN 问题等。以下是针对这些问题的一些解决方案: 1. 插件冲突问题描述:某些插件可能与 Z-Blog 核心或其他插件存在冲突,导致 JavaScript 无法正常加…

zblog 404.php 怎么写,zblogphp主题错误之模板选择

在 Z-Blog PHP 中,如果你想要自定义 404 页面(即当用户访问不存在的页面时显示的页面),你可以创建一个 404.php 文件。下面是创建一个简单的 404 页面的基本步骤和示例代码: 创建 404.php 文件创建文件:在你的主题目录中创建一个名为 404.php 的文件。 添加基本 HTML 结构…

zblog上传安装主题插件不成功的原因和解决办法

当在 Z-Blog 中上传安装主题或插件不成功时,可能的原因有很多。下面列出了一些常见的原因及相应的解决办法: 1. 文件大小超出限制问题描述:上传的文件包可能超出了服务器允许的最大文件大小。 解决方法:检查后台的“全局设置”中的“允许上传文件的大小”,并适当增大这个值…

本地调试多服务

本地idea如果同一个服务,想开多个服务,只需要改下端口重新启动,配置如下 1、打开idea的Services (老版本的idea应该是Run bashboard) 2、如图所示的两个stockApplication就是相同的服务,不同的端口 3、点击其中一个StockApplication8082右击找到Copy Configuration 这样…

zblog显示Allowed memory size of 6553652 bytes exhauste

当 Z-Blog 显示 “Allowed memory size of 6553652 bytes exhausted” 错误时,这意味着 PHP 的内存限制已达到上限。这种错误通常发生在处理大量数据或执行复杂操作时。以下是解决此问题的一些常见步骤: 1. 增加 PHP 内存限制 方法一:修改 php.ini 文件定位 php.ini 文件找到…

zblog应用中心连接失败的解决方案

当 Z-Blog 应用中心连接失败时,这通常意味着服务器无法正常访问应用中心的服务器。以下是解决此类问题的一些常见步骤: 1. 检查网络连接问题描述:网络连接可能不稳定或中断。 解决方法:确认服务器可以正常访问互联网。 尝试 ping 应用中心的服务器地址,例如:shping appce…

C++信奥老师解一本通题 1164:digit函数

​ 【题目描述】在程序中定义一函数digit(n,k),它能分离出整数n从右边数第k个数字。【输入】正整数n和k。【输出】一个数字。【输入样例】 31859 3 【输出样例】 8#include <iostream> using namespace std; int digit(long long n,int k) {if(k==1)return n%10;return …

zblog添加html后台出错,zblog后台设置固定域名绑定错误

当在 Z-Blog 中遇到添加 HTML 后台出错或后台设置固定域名绑定错误时,这通常意味着在配置或设置过程中出现了问题。以下是一些排查和解决步骤: 1. 检查域名绑定问题描述:域名可能未正确绑定到服务器。 解决方法:确认域名已正确绑定到服务器。 检查 DNS 设置,确保域名指向正…

zblog域名后无法访问 zblog数据库地址修改

当 Z-Blog 在修改域名后无法访问时,这通常意味着数据库中的某些配置没有正确更新。以下是解决此类问题的一些建议步骤: 1. 检查数据库配置问题描述:数据库配置可能未正确更新。 解决方法:登录到数据库管理工具(如 phpMyAdmin)。 找到 Z-Blog 的数据库。 检查数据库中的 z…

zblog后台编辑模块式时提示“UNKNOWN:未查询到相关数据”

当在 Z-Blog 后台编辑模块时提示 “UNKNOWN:未查询到相关数据”,这通常意味着后端查询数据库时未能找到预期的数据。以下是一些可能的原因及解决办法: 1. 数据库查询错误问题描述:查询语句可能有误或数据库表结构发生变化。 解决方法:检查数据库查询语句是否正确。 确认数据…

软件工程个人作业(第二次)

这个作业属于哪个课程 软件工程这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/SE2024/homework/13253这个作业的目标 熟悉并利用AIGC开发较为简单的小游戏学号 102202117项目介绍 Github仓库链接:https://github.com/nianyingyuan/Shao-a-shao 游戏介绍 1,游戏采用…

Z-Blog后台应用中心无法打开解决办法

如果 Z-Blog 后台的应用中心无法打开,这可能是由多种因素造成的。下面列出了一些常见的原因及解决办法: 1. 检查网络连接问题描述:网络连接不稳定或中断。 解决方法:检查网络连接是否正常。 尝试刷新页面或更换网络环境。2. 检查服务器防火墙设置问题描述:服务器防火墙阻止…