Advertisement
Inverness

XlsxReader.cs

Feb 27th, 2015
352
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.82 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Xml.Linq;
  6. using ICSharpCode.SharpZipLib.Zip;
  7.  
  8. namespace Ramp.Data
  9. {
  10.     /// <summary>
  11.     ///     Allows sheets to be read from Microsoft Excel files (.xlsx).
  12.     /// </summary>
  13.     public class XlsxReader : IDisposable
  14.     {
  15.         private readonly bool _keepOpen;
  16.         private readonly ZipFile _file;
  17.         private bool _haveGlobals;
  18.         private Dictionary<string, string> _sheetIds;
  19.         private Dictionary<string, string> _sharedStrings;
  20.  
  21.         public XlsxReader(string filename)
  22.             : this(new ZipFile(filename))
  23.         {
  24.         }
  25.  
  26.         public XlsxReader(Stream stream, bool keepOpen = false)
  27.             : this(new ZipFile(stream), keepOpen)
  28.         {
  29.         }
  30.  
  31.         public XlsxReader(ZipFile file, bool keepOpen = false)
  32.         {
  33.             Validate.ArgumentNotNull(file, "file");
  34.             _file = file;
  35.             _keepOpen = keepOpen;
  36.         }
  37.  
  38.         public void Dispose()
  39.         {
  40.             if (!_keepOpen)
  41.                 _file.Close();
  42.         }
  43.  
  44.         /// <summary>
  45.         ///     Gets the names of all sheets in the document.
  46.         /// </summary>
  47.         /// <returns> A collection containing the names of all sheets in the document. </returns>
  48.         public ICollection<string> GetSheetNames()
  49.         {
  50.             EnsureGlobals();
  51.             return _sheetIds.Keys;
  52.         }
  53.  
  54.         /// <summary>
  55.         ///     Reads the sheet with the specified name.
  56.         /// </summary>
  57.         /// <param name="name"> A sheet name. </param>
  58.         /// <returns> A two-dimensional array with the contents of the specified sheet. </returns>
  59.         public string[,] ReadSheet(string name)
  60.         {
  61.             EnsureGlobals();
  62.  
  63.             string id;
  64.             if (!_sheetIds.TryGetValue(name, out id))
  65.                 throw new ArgumentOutOfRangeException("name", "sheet not found: " + name);
  66.  
  67.             XDocument doc = LoadXmlEntry(String.Format("xl/worksheets/sheet{0}.xml", id));
  68.             // ReSharper disable once PossibleNullReferenceException
  69.             XNamespace ns = doc.Root.GetDefaultNamespace();
  70.  
  71.             XName rowElementName = ns + "row";
  72.             XName columnElementName = ns + "c";
  73.             XName valueElementName = ns + "v";
  74.             XName crefAttributeName = "r";
  75.             XName typeAttributeName = "t";
  76.  
  77.             int? columnCount = null;
  78.             var currentRow = new List<string>();
  79.             var rows = new List<string[]>();
  80.  
  81.             foreach (XElement row in doc.Descendants(rowElementName))
  82.             {
  83.                 foreach (XElement column in row.Elements(columnElementName))
  84.                 {
  85.                     var cref = (string) column.Attribute(crefAttributeName);
  86.                     var type = (string) column.Attribute(typeAttributeName);
  87.                     var value = (string) column.Element(valueElementName);
  88.  
  89.                     if (type == "s")
  90.                         value = _sharedStrings[value];
  91.  
  92.                     int columnIndex, rowIndex;
  93.                     ParseCellReference(cref, out columnIndex, out rowIndex);
  94.  
  95.                     if (currentRow.Count <= columnIndex)
  96.                         currentRow.Resize(columnIndex + 1);
  97.                     currentRow[columnIndex] = value;
  98.                 }
  99.  
  100.                 if (!columnCount.HasValue)
  101.                     columnCount = currentRow.Count;
  102.                 currentRow.Resize(columnCount.Value);
  103.                 rows.Add(currentRow.ToArray());
  104.                 currentRow.Clear();
  105.             }
  106.  
  107.             if (!columnCount.HasValue)
  108.                 return new string[0, 0];
  109.  
  110.             var result = new string[rows.Count, columnCount.Value];
  111.  
  112.             for (int r = 0; r < rows.Count; r++)
  113.                 for (int c = 0; c < columnCount; c++)
  114.                     result[r, c] = rows[r][c];
  115.  
  116.             return result;
  117.         }
  118.  
  119.         private void EnsureGlobals()
  120.         {
  121.             if (_haveGlobals)
  122.                 return;
  123.  
  124.             // Load all sheet names and ids from the workbook file
  125.             {
  126.                 XDocument doc = LoadXmlEntry("xl/workbook.xml");
  127.                 // ReSharper disable once PossibleNullReferenceException
  128.                 XNamespace ns = doc.Root.GetDefaultNamespace();
  129.  
  130.                 _sheetIds = new Dictionary<string, string>();
  131.  
  132.                 foreach (XElement sheet in doc.Descendants(ns + "sheet"))
  133.                 {
  134.                     var name = (string) sheet.Attribute("name");
  135.                     var sheetId = (string) sheet.Attribute("sheetId");
  136.                     _sheetIds.Add(name, sheetId);
  137.                 }
  138.             }
  139.  
  140.             // Load all shared strings
  141.             {
  142.                 XDocument doc = LoadXmlEntry("xl/sharedStrings.xml");
  143.                 // ReSharper disable once PossibleNullReferenceException
  144.                 XNamespace ns = doc.Root.GetDefaultNamespace();
  145.  
  146.                 _sharedStrings = new Dictionary<string, string>();
  147.  
  148.                 int index = 0;
  149.                 foreach (XElement text in doc.Descendants(ns + "t"))
  150.                 {
  151.                     _sharedStrings.Add(index.ToString(CultureInfo.InvariantCulture), text.Value);
  152.                     index++;
  153.                 }
  154.             }
  155.  
  156.             _haveGlobals = true;
  157.         }
  158.  
  159.         private XDocument LoadXmlEntry(string name)
  160.         {
  161.             ZipEntry wb = _file.GetEntry(name);
  162.             if (wb == null)
  163.                 throw new InvalidDataException("entry not found: " + name);
  164.  
  165.             using (var stream = _file.GetInputStream(wb))
  166.                 return XDocument.Load(stream);
  167.         }
  168.  
  169.         // Parse a cell reference in the format ABC123 to get a column and row index.
  170.         private static void ParseCellReference(string cr, out int columnIndex, out int rowIndex)
  171.         {
  172.             int firstDigit = -1;
  173.             for (int i = 0; i < cr.Length; i++)
  174.             {
  175.                 if (Char.IsDigit(cr[i]))
  176.                 {
  177.                     firstDigit = i;
  178.                     break;
  179.                 }
  180.             }
  181.             if (firstDigit == -1)
  182.                 throw new FormatException("invalid cell reference: " + cr);
  183.  
  184.             string columnName = cr.Substring(0, firstDigit);
  185.  
  186.             int columnNumber = 0;
  187.             int magnitude = columnName.Length - 1;
  188.  
  189.             foreach (char c in columnName)
  190.             {
  191.                 int val = c - 'A' + 1;
  192.                 int mult = magnitude != 0 ? (int) Math.Pow(26, magnitude) : 1;
  193.                 columnNumber += val * mult;
  194.                 magnitude--;
  195.             }
  196.  
  197.             columnIndex = columnNumber - 1;
  198.             rowIndex = int.Parse(cr.Substring(firstDigit)) - 1;
  199.         }
  200.     }
  201. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement