I am trying to get NPOI to work with ASP.NET (C#) and I want to read an excel file and put it in a DataSet. Here is the code I attempted:
public static DataTable getExcelData(string FileName, string strSheetName)
{
DataTable dt = new DataTable();
HSSFWorkbook hssfworkbook;
using (FileStream file = new FileStream(FileName, FileMode.Open, FileAccess.Read))
{
hssfworkbook = new HSSFWorkbook(file);
}
ISheet sheet = hssfworkbook.GetSheet(strSheetName);
System.Collections.IEnumerator rows = sheet.GetRowEnumerator();
while (rows.MoveNext())
{
IRow row = (HSSFRow)rows.Current;
if (dt.Columns.Count == 0)
{
for (int j = 0; j < row.LastCellNum; j++)
{
dt.Columns.Add(row.GetCell(j).ToString());
}
continue;
}
DataRow dr = dt.NewRow();
for (int i = 0; i < row.LastCellNum; i++)
{
ICell cell = row.GetCell(i);
if (cell == null)
{
dr[i] = null;
}
else
{
dr[i] = cell.ToString();
}
}
dt.Rows.Add(dr);
}
return dt;
}
The Error that I get is
+ $exception {"Object reference not set to an instance of an object."} System.Exception {System.NullReferenceException}
The odd thing is that this actually works with 2 excel files that I have, but when I put in a third one it crashes with that error.
This returns null if strSheetName isn't found:
ISheet sheet = hssfworkbook.GetSheet(strSheetName);
try:
for( int iSheet = 0; iSheet < hssfworkbook.NumberOfSheets; ++iSheet )
{
ISheet sheet = hssfworkbook.GetSheetAt(iSheet); // could cast to HSSFSheet
String strSheetNameActual = sheet.SheetName;
}
Then figure out how you want to compare strSheetName to strSheetNameActual or which sheets you want to process and how.
Try using this:
for (int j = row.FirstCellNum; j < row.LastCellNum; j++)
and
for (int i = row.FirstCellNum; i < row.LastCellNum; i++)
Instead of:
for (int j = 0; j < row.LastCellNum; j++)
and
for (int i = 0; i < row.LastCellNum; i++)
Also, make sure that you manage the case when the cells on the first row are null:
if (dt.Columns.Count == 0)
{
int empty = 0;
for (int j = row.FirstCellNum; j < row.LastCellNum; j++)
{
ICell cell = row.GetCell(j);
if (cell == null)
{
dt.Columns.Add(String.Format("emptyColumnName_{0}", empty++));
}
else
{
dt.Columns.Add(row.GetCell(j).ToString());
}
}
continue;
}
If you always want to read from the first sheet (probably, to get rid of the second method parameter, the sheet name, which is also the cause of your error), you may use:
// rest of the method's code
ISheet sheet = hssfworkbook.GetSheetAt(0);
if (sheet == null)
return dt;
var rows = sheet.GetRowEnumerator();
// rest of the method's code
Related
I am trying to Merge more excel document into a single document using NPOI. Here is the code write:``
static void Main(string[] args)
{
DataTable dt = new DataTable();
string[] files = new string[] { #"C:\Users\Ionut\source\repos\ExcelMergeDocument\ExcelMergeDocument\bin\Debug\TAMUExport\Project1\Report3Item.xls",
#"C:\Users\Ionut\source\repos\ExcelMergeDocument\ExcelMergeDocument\bin\Debug\TAMUExport\Project2\Report3Item.xls"};
for (int i = 0; i < files.Length; i++)
{
MergeData(files[i], dt);
}
ExportEasy(dt, finalImagePathReport3full);
}
public static string imagePathReport3full = #"\ResultReport3Item.xls";
public static string finalImagePathReport3full = AssemblyDirectory + imagePathReport3full;
public static string AssemblyDirectory
{
get
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return System.IO.Path.GetDirectoryName(path);
}
}
private static void MergeData(string path, DataTable dt)
{
XSSFWorkbook workbook = new XSSFWorkbook(path);
XSSFSheet sheet = (XSSFSheet)workbook.GetSheetAt(0);
XSSFRow headerRow = (XSSFRow)sheet.GetRow(0);
int cellCount = headerRow.LastCellNum;
if (dt.Rows.Count == 0)
{
for (int i = headerRow.FirstCellNum; i < cellCount; i++)
{
DataColumn column = new DataColumn(headerRow.GetCell(i).StringCellValue);
dt.Columns.Add(column);
}
}
else
{
}
int rowCount = sheet.LastRowNum + 1;
for (int i = (sheet.FirstRowNum + 1); i < rowCount; i++)
{
XSSFRow row = (XSSFRow)sheet.GetRow(i);
DataRow dataRow = dt.NewRow();
for (int j = row.FirstCellNum; j < cellCount; j++)
{
if (row.GetCell(j) != null)
dataRow[j] = row.GetCell(j).ToString();
}
dt.Rows.Add(dataRow);
}
workbook = null;
sheet = null;
}
public static void ExportEasy(DataTable dtSource, string strFileName)
{
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = (HSSFSheet)workbook.CreateSheet();
HSSFRow dataRow = (HSSFRow)sheet.CreateRow(0);
foreach (DataColumn column in dtSource.Columns)
{
dataRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);
}
for (int i = 0; i < dtSource.Rows.Count; i++)
{
dataRow = (HSSFRow)sheet.CreateRow(i + 1);
for (int j = 0; j < dtSource.Columns.Count; j++)
{
dataRow.CreateCell(j).SetCellValue(dtSource.Rows[i][j].ToString());
}
}
using (MemoryStream ms = new MemoryStream())
{
using (FileStream fs = new FileStream(strFileName, FileMode.Create, FileAccess.Write))
{
workbook.Write(fs);
}
}
}
}
When I am run I have the following error:
ICSharpCode.SharpZipLib.Zip.ZipException: 'Cannot find central
directory'
what did I suppose to do?
NPUI work only with .xlsx extension? I have the only .xls extension for the excel document. But where I run the program with .xlsx extension I have other error :
System.IO.InvalidDataException: 'Zip File is closed'
The both error appear on this line of code :
XSSFWorkbook workbook = new XSSFWorkbook(path);
This is the modified that I do on the code:
private static void MergeData(string path, DataTable dt)
{
// HSSFWorkbook workbook = new HSSFWorkbook(path);
HSSFWorkbook workbook;
using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read))
{
workbook = new HSSFWorkbook();
}
HSSFSheet sheet = (HSSFSheet)workbook.GetSheetAt(0);
HSSFRow headerRow = (HSSFRow)sheet.GetRow(0);
int cellCount = headerRow.LastCellNum;
if (dt.Rows.Count == 0)
{
for (int i = headerRow.FirstCellNum; i < cellCount; i++)
{
DataColumn column = new DataColumn(headerRow.GetCell(i).StringCellValue);
dt.Columns.Add(column);
}
}
else
{
}
int rowCount = sheet.LastRowNum + 1;
for (int i = (sheet.FirstRowNum + 1); i < rowCount; i++)
{
HSSFRow row = (HSSFRow)sheet.GetRow(i);
DataRow dataRow = dt.NewRow();
for (int j = row.FirstCellNum; j < cellCount; j++)
{
if (row.GetCell(j) != null)
dataRow[j] = row.GetCell(j).ToString();
}
dt.Rows.Add(dataRow);
}
workbook = null;
sheet = null;
}
Now at this line of code : `
HSSFSheet sheet = (HSSFSheet)workbook.GetSheetAt(0);
he tell me System.ArgumentOutOfRangeException:
'Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index'.
I read about this error and I know to initializate the index or create one, I tried that too, but no effect. What I do wrong or maybe where suppose to initializate the index?
Rest of the code is the same as the previos panel, only this void I modified to Merge .xls document.
I'm Trying to read Excel to DataTable using NPOI.Every thing working fine but only issue is If we have any Column cell is empty in that row it is not reading .In Excel i have 4 row's with (each row have some empty values for cells).
Excel File Image : enter image description here
After Reading That Excel To data table :enter image description here
I want like this in data table
private DataTable GetDataTableFromExcel(String Path)
{
XSSFWorkbook wb;
XSSFSheet sh;
String Sheet_name;
using (var fs = new FileStream(Path, FileMode.Open, FileAccess.Read))
{
wb = new XSSFWorkbook(fs);
Sheet_name = wb.GetSheetAt(0).SheetName; //get first sheet name
}
DataTable DT = new DataTable();
DT.Rows.Clear();
DT.Columns.Clear();
// get sheet
sh = (XSSFSheet)wb.GetSheet(Sheet_name);
int i = 0;
while (sh.GetRow(i) != null)
{
// add neccessary columns
if (DT.Columns.Count < sh.GetRow(i).Cells.Count)
{
for (int j = 0; j < sh.GetRow(i).Cells.Count; j++)
{
DT.Columns.Add("", typeof(string));
}
}
// add row
DT.Rows.Add();
// write row value
for (int j = 0; j < sh.GetRow(i).Cells.Count; j++)
{
var cell = sh.GetRow(i).GetCell(j);
DT.Rows[i][j] = sh.GetRow(i).GetCell(j);
}
i++;
}
return DT;
}
Plese hlp me.
you may have to try something along this line. its workable code to read the excel using NPOI.
// read the current row data
XSSFRow headerRow = (XSSFRow)sheet.GetRow(0);
// LastCellNum is the number of cells of current rows
int cellCount = headerRow.LastCellNum;
// LastRowNum is the number of rows of current table
int rowCount = sheet.LastRowNum + 1;
bool isBlanKRow = false;
//Start reading data after first row(header row) of excel sheet.
for (int i = (sheet.FirstRowNum + 1); i < rowCount; i++)
{
XSSFRow row = (XSSFRow)sheet.GetRow(i);
DataRow dataRow = dt.NewRow();
isBlanKRow = true;
try
{
for (int j = row.FirstCellNum; j < cellCount; j++)
{
if (null != row.GetCell(j) && !string.IsNullOrEmpty(row.GetCell(j).ToString()) && !string.IsNullOrWhiteSpace(row.GetCell(j).ToString()))
{
dataRow[j] = row.GetCell(j).ToString();
isBlanKRow = false;
}
}
}
catch (Exception Ex)
{
}
if (!isBlanKRow)
{
dt.Rows.Add(dataRow);
}
}
I am finding a running instance of excel and inserting data into one of the worksheets. Everything works, however inserting data is very slow. ~0.25 seconds for each cell.
Is there any way I can make this faster?
I have been looking for an approach that doesn't go cell by cell, but haven't found anything.
My code:
using System;
using System.Data;
using Microsoft.Office.Interop.Excel;
using Application = Microsoft.Office.Interop.Excel.Application;
using DataTable = System.Data.DataTable;
[STAThread]
static void Main()
{
Application xlApp = null;
try
{
xlApp = (Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
}
catch (Exception)//Excel not open
{
return;
}
xlApp.Visible = true;
var wb = xlApp.ActiveWorkbook;
Worksheet ws = null;
try
{
ws = (Worksheet)wb.Worksheets["SomeSheet"];
}
catch (Exception e)
{
return;
}
if (ws == null)
{
return;
}
var dt = new DataTable();
dt = new DataTable();
dt.Clear();
dt.Columns.Add("Col1");
dt.Columns.Add("Col2");
for (int ii = 0; ii < 20; ii++)
{
DataRow row = dt.NewRow();
row["Col1"] = "xxxx";
row["Col2"] = "yyyy";
dt.Rows.Add(row);
}
//Header
for (int i = 0; i < dt.Columns.Count; i++)
{
ws.Cells[1, i + 1] = dt.Columns[i].ColumnName;
}
//Data
for (int i = 0; i < dt.Rows.Count; i++)
{
for (int j = 0; j < dt.Columns.Count; j++)
{
ws.Cells[i + 2, j + 1] = dt.Rows[i][j];
}
}
}
Have you tried using a Range? Assigning a Array to the range ensuring the Range and Array are the same length should work.
https://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.worksheet.range.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
I am trying to export a database from c# to excel but the first row from the database is not saving in excel.
private void exporttoexcel()
{
Microsoft.Office.Interop.Excel._Application excel = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel._Workbook workbook = excel.Workbooks.Add(Type.Missing);
Microsoft.Office.Interop.Excel._Worksheet worksheet = null;
try
{
worksheet = workbook.ActiveSheet;
worksheet.Name = "ExportedFromDatGrid";
int cellRowIndex = 1;
int cellColumnIndex = 1;
//Loop through each row and read value from each column.
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
for (int j = 0; j < dataGridView1.Columns.Count; j++)
{
// Excel index starts from 1,1. As first Row would have the Column headers, adding a condition check.
if (cellRowIndex == 1)
{
worksheet.Cells[cellRowIndex, cellColumnIndex] = dataGridView1.Columns[j].HeaderText;
}
else
{
worksheet.Cells[cellRowIndex, cellColumnIndex] = dataGridView1.Rows[i].Cells[j].Value.ToString();
}
cellColumnIndex++;
}
cellColumnIndex = 1;
cellRowIndex++;
}
}
catch(Exception ex)
{
}
}
here is the code I'm using. could anyone help me ? I am new in coding.
You're not writing out the data but are only writing out column names when the cellColumnIndex is 1, skipping the first row. But after the first row has been processed, the row index will be incremented. Refactor your for-loop to look something like this:
// Add the column names
var index = 0;
foreach(var column in dataGridView1.Columns)
{
worksheet.Cells[0, index] = column.HeaderText;
index++;
}
//Loop through each row and read value from each column.
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
for (int j = 0; j < dataGridView1.Columns.Count; j++)
{
// Excel index starts from 1,1. As first Row would have the Column headers, adding a condition check.
worksheet.Cells[cellRowIndex, cellColumnIndex] = dataGridView1.Rows[i].Cells[j].Value.ToString();
cellColumnIndex++;
}
cellColumnIndex = 1;
cellRowIndex++;
}
Please have a look at ClosedXML. It simplifies writing your code, and eliminate the need to have Excel installed on the machine where you want to run this.
I was trying to put loop through and put CSV to 2D array.
My app crashes due to var tokens = sr.ReadLine().Split(','); It throws a NullPointerException. How can I fix this?
Below is my whole method named csvToArray:
public string[,] csvToArray (string filePath)
{
int col = colCount(filePath);
int row = rowCount(filePath);
string line;
string[,] data = new string[col, row];
using (StreamReader sr = new StreamReader(filePath))
{
for (int i = 0; i < col; i++)
{
var tokens = sr.ReadLine().Split(',');
for (int j = 0; j < row; j++)
{
data[i, j] = tokens[j];
}
}
}
return data;
}
What does not make sense is that it finished the whole loop. The variables below the exception have the values that they were supposed to have.
You have to use first for loop for row and then inner loop for column.
public static string[,] csvToArray(string filePath)
{
int col = colCount(filePath);
int row = rowCount(filePath);
string line;
string[,] data = new string[row, col];
using (StreamReader sr = new StreamReader(filePath))
{
for (int i = 0; i < row; i++)
{
var tokens = sr.ReadLine().Split(',');
for (int j = 0; j < col; j++)
{
data[i, j] = tokens[j];
}
}
}
return data;
}
In your code you are doing null.Split(), that is why it giving you exception.
If you want to insert 0 in each cell for a blank row, then you can implement the following code.
for (int i = 0; i < row; i++)
{
string content = sr.ReadLine();
if (!string.IsNullOrEmpty(content))
{
var tokens = content.Split(',');
for (int j = 0; j < col; j++)
{
data[i, j] = tokens[j];
}
}
else
{
for (int j = 0; j < col; j++)
{
data[i, j] = "0";
}
}
}
Lets first analyse errors in your example.
1) Why do you event need to know length of row and columns in the beggining? It is overhead.
2) Row and columns in your loop is invalid.
3) This exception throws in your example because your reached EOF.
So, here is better way to read csv to 2D matrix:
public int[][] csvToArray (string filePath)
{
string line = null;
var result = new List<int[]>();
using (var sr = new StreamReader(filePath))
{
while((line = sr.ReadLine()) != null)
{
if(string.IsNullOrWhiteSpace(line)) continue;
result.Add(sr.Split(',').Select(x=> string.IsNullOrWhiteSpace(x) ? 0 : int.Parse(x)).ToArray());
}
}
return result.ToArray();
}
Then you can just check your matrix for consistency.
At least, this way you won't open your file three times and protected from counting errors.