最近来了一个需求, 需要手动解析word ( 好处就是不需要安装office 以及不会有office解析的线程残留),然后就是可以自定义解析规则,比较方便
比如解析这个word里面的内容: 标题,表格的行和列,以及单元格里面的每一个项
解决方案
使用 DocumentFormat.OpenXml.dll + WindowsBase.dll+正则表达式
WindowsBase.dll 和 DocumentFormat.OpenXml.dll 都是微软的,
可以不用安装office 就能得到 对应的 文档( word,excel) 的 xml格式文本内容,缺点是 只支持 docx,xlsx ,
低版本的(doc,xls)读不出来(可能是未按照对应的协议进行排版),
另外再说一个比较 麻烦的问题, 金山wps和 office ,以及其他厂商的,对 word文件 的内部实现不一样,解析的话需要做兼容处理.
以下是主要的对象以及方法:(仅供参考)
//引入命名空间
using System.Text.RegularExpressions;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using (WordprocessingDocument doc = WordprocessingDocument.Open(stream, false)) { Body body = doc.MainDocumentPart.Document.Body; foreach (var xmlElement in body.Elements<OpenXmlElement>()) { List<string> RowElement = new List<string>(); foreach (var item in xmlElement.ChildElements) { string elementText = item.InnerText.Trim(); if (!string.IsNullOrEmpty(elementText)) { //处理 elementText RowElement.Add(elementText); } } } } string bodyInnerXml = body.InnerXml; //然后通过 正则表达式 "<w:tbl(\s|>)(.+?)</w:tbl>" 得到 table的个数 /*2.解析 table 对应的 单元格数据*/ foreach (var table in body.Elements<Table>()) { string t_table_xml = table.InnerXml; //通过正则 "<w:tr(\s|>)(.+?)</w:tr>"); 匹配行的个数 //通过正则"<w:tr(\s|>)(.+?)</w:tr>", "<w:tc>(.+?)</w:tc>" 匹配列的个数 }
//遍历行 foreach (var tableRow in table.Elements<TableRow>()) { string rowInnerText = tableRow.InnerText; //遍历列 foreach (var tableCell in tableRow.Elements<TableCell>()) { //凡是实现了IEnumerable接口的类,都可以使用foreach循环迭代遍历, 不过很遗憾,没有提供 this[index] 的访问方式 string celInnerXml = tableCell.InnerXml; //要解析 单个item项的内容. 只能通过 正则表达式了, 不同的厂商, 里面的xml内容是不一样的, 所以要做很多的兼容.... if ((celInnerXml.Contains("<w:numPr>")|| celInnerXml.Contains("w:pPr>"))&& celInnerXml.Contains("</w:p>"))
{
//1.目前来说是这样做兼容的
//2.然后通过正则 "<w:numPr(\s|>)(.+?)</w:p>" 和 "<w:pPr(\s|>)(.+?)</w:p>" 判断是否有对应的 匹配项
//3.就算得到了 匹配项,. 里面还有很多的样式代码,要把这些样式代码都替换掉(通过正则 @"<(.[^>]*)>" 进行替换)
//4.另外还有各种有序符号,无序符号,以及 checkbox框( □ ) 等 标识符, 这些字符贴到网页上 的code值好像都是9633
// 5.在 word里面 无序符号 是通过特殊的标签定义的,不同厂商的标签不一样,要识别不同厂商的只能做兼容处理
} } }
贴3个比较有用的方法
/// <summary> /// 去除所有Html标签,以及换行,制表符 /// </summary> /// <param name="Htmlstring">要格式化的字符串</param> /// <returns></returns> public static string NoHTML(string Htmlstring) //去除HTML标记 { //删除脚本 Htmlstring = Regex.Replace(Htmlstring, @"<script.+?</script>", "", RegexOptions.Multiline | RegexOptions.IgnoreCase); //删除HTML Htmlstring = Regex.Replace(Htmlstring, @"<(.[^>]*)>", "", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"-->", "", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"<!--.*", "", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&(quot|#34);", "/", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&(amp|#38);", "&", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&(lt|#60);", "<", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&(gt|#62);", ">", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&(nbsp|#160);", " ", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&(iexcl|#161);", "/xa1", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&(cent|#162);", "/xa2", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&(pound|#163);", "/xa3", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&(copy|#169);", "/xa9", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @"&#(/d+);", "", RegexOptions.IgnoreCase); Htmlstring = Regex.Replace(Htmlstring, @" | | ", ""); return Htmlstring; }
public static string NoXml(string xmlString,string replaceEmpty="")//去除xml标记 { //删除脚本 // xmlString = Regex.Replace(xmlString, @"<script.+?</script>", "", RegexOptions.Multiline | RegexOptions.IgnoreCase); //删除<>标签内的内容 xmlString = Regex.Replace(xmlString, @"<(.[^>]*)>", "", RegexOptions.IgnoreCase); if (replaceEmpty.Length>0) { xmlString = xmlString.Replace(replaceEmpty, ""); } xmlString = xmlString.Replace("□", ""); return xmlString; }
/// <summary> /// 得到所有(.+?)匹配到的集合 /// </summary> /// <param name="regexStr">带(.+?)的正则表达式</param> /// <param name="inputHtml">Html源代码</param> /// <returns></returns> public static List<string> GetMatchRegexList(string regexStr, string inputHtml) { List<string>list = new List<string>(); StringBuilder sbulider = new StringBuilder(); if (!string.IsNullOrEmpty(regexStr)) { Regex regex = new Regex(regexStr, RegexOptions.Singleline | RegexOptions.IgnoreCase); if (regex.IsMatch(inputHtml)) { MatchCollection mc = regex.Matches(inputHtml); for (int i = 0; i < mc.Count; i++) { list.Add(mc[i].Groups[0].Value); } } } return list; }
GetInnerHtml(p_text:string):string{ //p_text = p_text.replace(//g,"@rn"); p_text = p_text.replace(/□/g,''); //这个是 9633 p_text = p_text.replace(/<span style="white-space:pre">/g,'@rn');//兼容处理 p_text = p_text.replace(/<br>/g,'@rn');//换行 p_text = p_text.replace(/</br>/g,'@rn');//换行 p_text = p_text.replace(/</p>/g,'</p>@rn');//换行 p_text = p_text.replace(/<(.[^>]*)>/g,'');//去除<>标签块的内容 p_text = p_text.replace(/@rn /g,'@rn'); //兼容处理 p_text = p_text.replace(/n /g,''); //兼容处理,一些稀奇古怪的东西 p_text = p_text.replace(/@rn/g,'@rn');//多个换行,替换为一个换行 //... 其他的兼容性代码 return p_text; } //其他代码 // console.log(("□".charCodeAt(0))); //无序符号,粘贴到网页里面, 变成了 这个字符, 经测定,该字符的 code值 为 9633
另外如果是 直接将word里面的内容, 粘贴到 网页上的div里面, 然后获取innerHTML 代码, 传到后台, 需要做一下预处理
比如我这里标记换行, 用的是 "@rn" 字符