如何使用剪贴板将数据从Excel表格复制到DataTable?
我有一个Winform项目,在Microsoft Framework 3.5上创build。 用户可能已经安装了Windows 7或Windows XP,以及Office 2007或更高版本。
我正在做一个程序来获取剪贴板数据,并放在一个C#数据表。 我已经创build了一个从剪贴板获取原始数据并将其上传到DataTable的方法。
但在某些情况下,Excel数据显示一个值,但内部有另一个值:
我正在研究一种从Excel中获取原始数据的方法:
string XmlFmt = "XML Spreadsheet"; var clipboard = Clipboard.GetDataObject(); if (clipboard.GetDataPresent(XmlFmt)) { var clipData = clipboard.GetData(XmlFmt); StreamReader streamReader = new StreamReader((MemoryStream)clipData); streamReader.BaseStream.SetLength(streamReader.BaseStream.Length - 1); string xmlText = streamReader.ReadToEnd(); var stream = new StringReader(xmlText); XmlDocument xmlDocument = new XmlDocument(); xmlDocument.LoadXml(xmlText); DataSet dsExcelData = new DataSet(); dsExcelData.ReadXml(new XmlNodeReader(xmlDocument)); }
但是,这个方法使用Excel数据的每个部分的configuration来检索具有多个表的DataSet:
基本上,我想将这些结构转换为只有原始数据的简单数据表。 有人可以帮我一个提示如何实现这一目标? …我不想在这个实现中使用第三方库。
如果他们是平坦的数据,你可以这样做。
private class Field { public string Valor { get; set; } } private class Row { public List<Field> Fields { get; set; } public Row(string value) { Fields = new List<Field>(); var fieldsString = value.Split(new char[] {'\t'}); foreach (string f in fieldsString) { Fields.Add(new Field {Valor = f}); } } } public Parse() { var data = Clipboard.GetDataObject(); var datos = (string)data.GetData(DataFormats.Text); var stringRows = datos.Split(new Char[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries); var table = new List<Row>(stringRows.Length) ; foreach (string stringRow in stringRows) { table.Add( new Row(stringRow) ); } }
在某些情况下,Excel数据显示一个值,但内部有另一个值。
使用XML方法是多个数据表内部结构的原因。 试试这个方法:
private void PasteFromExcel() { DataTable tbl = new DataTable(); tbl.TableName = "ImportedTable"; List<string> data = new List<string>(ClipboardData.Split('\n')); bool firstRow = true; if (data.Count > 0 && string.IsNullOrWhiteSpace(data[data.Count - 1])) { data.RemoveAt(data.Count - 1); } foreach (string iterationRow in data) { string row = iterationRow; if (row.EndsWith("\r")) { row = row.Substring(0, row.Length - "\r".Length); } string[] rowData = row.Split(new char[] { '\r', '\x09' }); DataRow newRow = tbl.NewRow(); if (firstRow) { int colNumber = 0; foreach (string value in rowData) { if (string.IsNullOrWhiteSpace(value)) { tbl.Columns.Add(string.Format("[BLANK{0}]", colNumber)); } else if (!tbl.Columns.Contains(value)) { tbl.Columns.Add(value); } else { tbl.Columns.Add(string.Format("Column {0}", colNumber)); } colNumber++; } firstRow = false; } else { for (int i = 0; i < rowData.Length; i++) { if (i >= tbl.Columns.Count) break; newRow[i] = rowData[i]; } tbl.Rows.Add(newRow); } } DataGridView1.DataSource = tbl; }
参考: http : //www.seesharpdot.net/? p= 221
编辑:
我做了一些testing,甚至使用“XML Spreadsheet”剪贴板格式的数据可以以指数表示法存储:
您可以检测并转换这些数字: 从指数表示法parsing数字
我发现了一个干净的防弹解决scheme。 这里代码:
首先,将XmlDocument转换为XElement的扩展:
/// <summary> Convert XML Document to XDocument </summary> /// <param name="xmlDocument">Attached XML Document</param> public static XDocument fwToXDocument(this XmlDocument xmlDocument) { using (XmlNodeReader xmlNodeReader = new XmlNodeReader(xmlDocument)) { xmlNodeReader.MoveToContent(); return XDocument.Load(xmlNodeReader); } }
完整的function:
private DataTable clipboardExcelToDataTable(bool blnFirstRowHasHeader = false) { string strTime = "S " + DateTime.Now.ToString("mm:ss:fff"); var clipboard = Clipboard.GetDataObject(); if (!clipboard.GetDataPresent("XML Spreadsheet")) return null; strTime += "\r\nRead " + DateTime.Now.ToString("mm:ss:fff"); StreamReader streamReader = new StreamReader((MemoryStream)clipboard.GetData("XML Spreadsheet")); strTime += "\r\nFinish read " + DateTime.Now.ToString("mm:ss:fff"); streamReader.BaseStream.SetLength(streamReader.BaseStream.Length - 1); XmlDocument xmlDocument = new XmlDocument(); xmlDocument.LoadXml(streamReader.ReadToEnd()); strTime += "\r\nRead XML Document " + DateTime.Now.ToString("mm:ss:fff"); XNamespace ssNs = "urn:schemas-microsoft-com:office:spreadsheet"; DataTable dtData = new DataTable(); var linqRows = xmlDocument.fwToXDocument().Descendants(ssNs + "Row").ToList<XElement>(); for (int x = 0; x < linqRows.Max(a => a.Descendants(ssNs + "Cell").Count()); x++) dtData.Columns.Add("Column {0}".fwFormat(x + 1)); int intCol = 0; DataRow drCurrent; linqRows.ForEach(rowElement => { intCol = 0; drCurrent = dtData.Rows.Add(); rowElement.Descendants(ssNs + "Cell") .ToList<XElement>() .ForEach(cell => drCurrent[intCol++] = cell.Value); }); if (blnFirstRowHasHeader) { int x = 0; foreach (DataColumn dcCurrent in dtData.Columns) dcCurrent.ColumnName = dtData.Rows[0][x++].ToString(); dtData.Rows.RemoveAt(0); } strTime += "\r\nF " + DateTime.Now.ToString("mm:ss:fff"); return dtData; }
这个过程大约需要15秒来读取〜25,000行。
适用于任何types的数据。 基本上,该方法创build与Excel WorkSheet相同结构的网格。 合并行或列将填满第一个单元格。 所有列默认都是stringDataType。