在C#应用程序中从Excel粘贴内容,保留完整的精度

9

我在Excel电子表格中有这样的数值数据:

  • 0.69491375
  • 0.31220394

这些单元格被格式化为百分比,并设置为显示两位小数。所以它们在Excel中显示为:

  • 69.49%
  • 31.22%

我有一个用C#编写的程序,可以从剪贴板解析出这些数据。

var dataObj = Clipboard.GetDataObject();
var format = DataFormats.CommaSeparatedValue;

if (dataObj != null && dataObj.GetDataPresent(format))
{
    var csvData = dataObj.GetData(format);
    // do something
}

问题在于csvData包含来自Excel的显示值,即“69.49%”和“31.22%”。它不包含额外小数位的完整精度。
我尝试使用各种不同的DataFormats值,但数据始终只包含来自Excel的显示值,例如:
- DataFormats.Dif - DataFormats.Rtf - DataFormats.UnicodeText - 等等
作为测试,我安装了LibreOffice Calc,并将相同的单元格从Excel复制/粘贴到Calc中。 Calc保留了原始数据的完整精度。
因此,很明显Excel将此数据放在其他程序可以访问的位置。我如何从我的C#应用程序中访问它?
编辑-下一步。
我已下载了LibreOffice Calc源代码,并将检查一下是否可以找到他们如何从Excel获取复制数据的完整上下文。
我还对从剪贴板返回的数据对象进行了GetFormats()调用,并得到了24个不同数据格式的列表,其中一些不在DataFormats枚举中。这些格式包括像Biff12Biff8Biff5Format129等我不熟悉的格式,因此如果我发现任何发现,我会进行调查并回复...

我刚刚尝试将数据粘贴到10个不同的程序中,它们都给了我格式化后的数据"69.00%\r\n",就像我在Clipboard.GetData中看到的一样。 - Tomislav Markovski
是的,你说得对。我在想,Excel是否有自己的内部剪贴板,用于存储所有格式和精度数据,而这些数据不会放在通用剪贴板上?Calc是否有一些钩子连接到内部Excel剪贴板,大多数应用程序都没有,并且很难访问?我担心这个答案。 :-) - Lyall
1
如果Calc确实使用了一个Excel钩子,那么我不知道为什么你不能使用同样的钩子... - Adam
2
我不是Excel专家,但我认为Excel很可能使用自定义剪贴板格式。我建议您尝试调用dataObj.GetFormats()来查看对象包含的格式,并从那里继续。希望这能帮助您进一步。 - Onots
感谢您的评论,我会跟进并在发现任何问题时发布。 - Lyall
我曾经问过相反的问题 - 如何将C#应用程序中的数据复制到Excel中 - 在http://stackoverflow.com/questions/13647945/how-use-clipboard-to-move-data-from-net-application-to-excel上。 - Colonel Panic
2个回答

6

虽然并非完整答案,但以下是有关问题的进一步见解:

当您复制单个 Excel 单元格时,剪贴板中将包含一个完整的 Excel 工作簿,其中包含一个电子表格,该电子表格又包含一个单元格:

var dataObject = Clipboard.GetDataObject();
var mstream = (MemoryStream)dataObject.GetData("XML Spreadsheet");

// Note: For some reason we need to ignore the last byte otherwise
// an exception will occur...
mstream.SetLength(mstream.Length - 1);

var xml = XElement.Load(mstream);

现在,当您将XElement的内容转储到控制台时,您可以看到您确实获得了完整的Excel工作簿。此外,“XML电子表格”格式包含存储在单元格中的数字的内部表示。因此,我想您可以使用Linq-To-Xml或类似的方法来获取所需的数据:

XNamespace ssNs = "urn:schemas-microsoft-com:office:spreadsheet";

var numbers = xml.Descendants(ssNs + "Data").
              Where(e => (string)e.Attribute(ssNs + "Type") == "Number").
              Select(e => (double)e);

我也尝试使用Excel Data Reader阅读Biff格式,但结果总是为空的DataSets...


(说明:Biff格式为Excel二进制文件格式,数据集(DataSets)指一组表格数据的容器)

我最终采用了这种方法,感谢您的见解。有趣的是,DataFormats枚举不包含“XML电子表格”,但通过对XML进行一些解析,它可以很好地工作。 - Lyall

4
BIFF格式是由Microsoft开放的规范(请注意,我说的是规范而不是标准)。阅读this以了解正在发生的事情。

那些BIFF你看到的对应于一些Excel格式。BIFF5是来自Excel 5.0和95的XLS,BIFF8是来自Excel 97到2003的XLS,BIFF12是来自Excel 2003的XLSB,需要注意的是Excel 2007也可以生成它们(我猜Excel 2010也可以)。这里有一些文档在这里,还有在这里(来自OpenOffice),可能会帮助你理解其中的二进制内容...

无论如何,过去已经有一些关于在C++、Java、VB和C#中解析这些文档的工作。例如,BIFF12 Reader、项目NExcelExcelLibrary等。

特别是NExcel将允许您传递一个流,您可以从剪贴板数据创建该流,然后查询NExcel以获取数据。如果您要获取源代码,那么我认为ExcelLibrary更易读。

您可以像这样获取流:

var dataobject = System.Windows.Forms.Clipboard.GetDataObject();
var stream = (System.IO.Stream)dataobject.GetData(format);

使用NExcel从流中读取表单的示例代码如下:
var wb = getWorkbook(stream);
var sheet = wb.Sheets[0];
var somedata = sheet.getCell(0, 0).Contents;

我猜微软的实际Office库也可以使用。

我知道这不是全部的故事,请分享进展情况。如果有机会,我会尝试它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接