2017-05-04 158 views
3

我有一个Winform项目,在Microsoft Framework 3.5上创建。 用户可能已经安装了Windows 7或Windows XP以及Office 2007或更高版本。如何使用剪贴板将数据从Excel表格复制到DataTable?

我正在开发一个过程来获取剪贴板数据并放入C#DataTable中。 我已经创建了一个方法从剪贴板获取原始数据并将其上传到DataTable中。

但在某些情况下,Excel数据显示的值,但内部有另一个:

enter image description here

我调查的方法,以从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)); 
} 

但是,此方法使用Excels数据的每个部分的配置来检索具有多个表的DataSet: enter image description here enter image description here

基本上,我想将这些结构转换为只有原始数据的简单DataTable。 有人可以帮助我一个提示如何实现这一目标? ...我不想在这个实现中使用第三方库。

+0

dsExcelData.Tables [0]将为您提供第一个基础数据表 – MethodMan

回答

0

我发现了一个干净的防弹解决方案。下面的代码:

首先,延伸到的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); 
    } 
} 

完整的功能:

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行。

适用于任何类型的数据。 基本上,该方法创建一个具有相同结构的Excel WorkSheet的网格。 合并行或列将填满第一个单元格。 默认情况下,所有列都将是字符串DataType。

2

如果他们是平坦的数据,你可以这样做。

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)); 
    } 
} 
+0

谢谢,但不适合我的需要。尝试获取剪贴板数据为文本将检索包含“掩码”的值。以值“12312451512412”为例的单元格将检索到“1.23125E + 13”。或者“0%”而不是“0.3%”。 – MiBol

0
在某些情况下

,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

编辑:

我已经做了一些测试,甚至使用“XML电子表格”剪贴板格式的数据可以得到存储在指数注释:

enter image description here

你可以检测nd转换这些数字:Parse a Number from Exponential Notation

+0

感谢分享,但无效。在这个例子中,剪贴板数据的单元格值为“1.23125E + 13”,但单元格的实际值为“12312451512412”。这是我想以XML SpreadSheet的形式读取剪贴板的主要原因之一。 – MiBol