问题点:NPOI处理xlsx文档时,将\r写成了换行符。

实例:以下字符abc\rcde

如果直接复制到Excel 2016,显示结果如下(单元格设置为折行显示):

如果用NPOI写入Xlsx文档,显示结果如下(单元格设置为折行显示):

代码如下:

            string path = @"C: \Users\Desktop\test.xlsx";

            var book = new XSSFWorkbook();
var sheet = book.CreateSheet("test");
var row = sheet.GetRow() ?? sheet.CreateRow();
var cell = row.GetCell() ?? row.CreateCell(); cell.SetCellValue("abc\rcde"); using (var file = new FileStream(path, FileMode.Create, FileAccess.Write))
{
book.Write(file);
file.Close();
}

查看生成的Excel内部数据确实成了换行符:

原因

OOXML因为使用XML格式存储数据,所以XML中无法表示的字符需要转换为Unicode码存储,Excel打开时会自动将这些Unicode码转换为原来的字符显示。由于NPOI需要兼容以前版本Excel,而没有处理'\t'  '\n'  '\r'这几个字符。

NPOI源码:

        public static string ExcelEncodeString(string t)
{
StringWriter sw = new StringWriter();
//poi dose not add prefix _x005f before _x????_ char.
//if (Regex.IsMatch(t, "(_x[0-9A-F]{4,4}_)"))
//{
// Match match = Regex.Match(t, "(_x[0-9A-F]{4,4}_)");
// int indexAdd = 0;
// while (match.Success)
// {
// t = t.Insert(match.Index + indexAdd, "_x005F");
// indexAdd += 6;
// match = match.NextMatch();
// }
//}
for (int i = ; i < t.Length; i++)
{
if (t[i] <= 0x1f && t[i] != '\t' && t[i] != '\n' && t[i] != '\r') //Not Tab, CR or LF
{
//[0x00-0x0a]-[\r\n\t]
//poi replace those chars with ?
sw.Write('?');
//sw.Write("_x00{0}_", (t[i] < 0xa ? "0" : "") + ((int)t[i]).ToString("X"));
}
else if (t[i] == '\uFFFE')
{
sw.Write('?');
}
else
{
sw.Write(t[i]);
}
}
return sw.ToString();
}

对应方法

Unicode表里面需要处理的部分:

遍历所有字符,将001f内的字符都转换为Unicode。

字符转换为Unicode代码:

        private static string EncodeXmlUTF(string value)
{
var builder = new StringBuilder();
foreach (char c in value.ToCharArray())
{
if (c < )
{
builder.Append($"_x{(c < 16 ? "" : "")}{Convert.ToInt32(c):X}_");
}
else
{
builder.Append(c);
}
}
return builder.ToString();
}

NPOI的场合

读取端:由于NPOI已经做了转换处理,所有不需要特别的代码。

写入端:

cell.SetCellValue(EncodeXmlUTF(text));

设置多文本的特殊处理:因为NPOI里面需要用到字符串位置信息,所有在它处理之后替换原先字符为Unicode。

            var text = new XSSFRichTextString("abcefg\rhijklmn");
text.ApplyFont(commonFont.Index);
text.ApplyFont(, , green_font); foreach (var r in text.GetCTRst().r)
{
r.t = EncodeXmlUTF(r.t);
}

OpenXML的场合

需要在SharedStringTable中写入SharedStringItem:

shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new Text(EncodeXmlUTF(value))));

读取的时候同理需要将SharedStringItem.InnerText转码后的数据转换回来:

Unicode转换回来代码:

        static String UtfDecode(String value)
{
if (value == null) return null; StringBuilder buf = new StringBuilder();
MatchCollection mc = utfPtrn.Matches(value);
int idx = ;
for (int i = ; i < mc.Count;i++ )
{
int pos = mc[i].Index;
if (pos > idx)
{
buf.Append(value.Substring(idx, pos-idx));
} String code = mc[i].Groups[].Value;
int icode = Int32.Parse(code, System.Globalization.NumberStyles.AllowHexSpecifier);
buf.Append((char)icode); idx = mc[i].Index+mc[i].Length;
}
buf.Append(value.Substring(idx));
return buf.ToString();
}

最新文章

  1. 录像时调用MediaRecorder的start()时发生start failed: -19错误
  2. Java应用程序项目的打包与发行(run.bat形式)
  3. BackGroundWorker控件的使用注意
  4. ACdream1063——平衡树
  5. UNIX网络编程-基本API介绍(一)
  6. centos 7 的几点改动
  7. js eval()执行传参函数的写法
  8. Android:从程序员到架构师之路Ⅰ
  9. Winform获取应用程序的当前路径
  10. XSS完全解决方案
  11. Unix Domain Socket 域套接字实现
  12. gen_compile.sql
  13. 团队作业4——第一次项目冲刺(Alpha版本)
  14. BigDecimal常用的加减乘除算法、比较大小、保存两位小数点
  15. oracle数据库查看和解除死锁
  16. 剑指offer(18)二叉搜索树的后续遍历
  17. VMware扩展Linux根目录磁盘空间(Centos版本)
  18. 算法笔记_225:数字密码发生器(Java)
  19. python渗透测试工具集合
  20. window.history的跳转实质-HTML5 history API 解析

热门文章

  1. 【JAVA】可视化计算器
  2. 学习笔记51_MongoDB使用
  3. OV5640摄像头的数据处理配置流程(一)
  4. [考试反思]0917csp-s模拟测试45:天命
  5. ubuntu16安装docker环境详细说明
  6. JavaScript中继承的实现方法--详解
  7. Vue组件间通信方式到底有几种
  8. Vue+element UI实现表格数据导出Excel组件
  9. NOIP模拟赛 华容道 (搜索和最短路)蒟蒻的第一道紫题
  10. SSHD服务安全的连接