C# Excel Trim worksheet cell when passing to a datatable? - c#

Here is the code below i'm using, how do you trim while doing the convert.ToString()?
while (((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, 1]).Value2 != null)
{
rowIndex = 2 + index;
row = excel_Holding_Table.NewRow();
for (int i = 1; i <= range.Columns.Count; i++)
{
row[i-1] = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, i]).Value2);
}
excel_Holding_Table.Rows.Add(row);
index++;
}

Just change the below line..Add the Trim() function at the last
row[i-1] = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, i]).Value2);
to
string temp = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, i]).Value2);
if(temp!=null)
row[i-1] =temp.Trim();
else
row[i-1] = "";

Related

Reading .txt file content to DataTable with Column headers on first line

I am trying to load data from a .txt file which looks like this:
|ABC|DEF|GHI|
|111|222|333|
|444|555|666|
With code:
using (StringReader reader = new StringReader(new StreamReader(fileStream, Encoding.Default).ReadToEnd()))
{
string line;
//reader.ReadLine(); //skip first line
while (reader.Peek() != -1)
{
line = reader.ReadLine();
if (line == null || line.Length == 0)
continue;
string[] values = line.Split('|').Skip(1).ToArray();
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
}
DataRow row = table.NewRow();
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
products++;
}
}
The problem is, when I generate a DataTable, I have first line as Column, but first line:
|ABC|DEF|GHI|
is visible also in the rows:
How to put first line as column headers and rest as rows?
I do not want to use CSVHelper for that if it possible.
Just need to skip when the first line after header is created
string line;
bool bheader= false;
//reader.ReadLine(); //skip first line
while (reader.Peek() != -1)
{
line = reader.ReadLine();
if (line == null || line.Length == 0)
continue;
string[] values = line.Split('|').Skip(1).ToArray();
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
bheader = true;
}
if(bheader ==false){
DataRow row = table.NewRow();
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
products++;
}
}
bheader = false;
}
The issue with your current code is that you handle when isColumnCreated is false, but not true. If you change this:
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
}
DataRow row = table.NewRow();
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
products++;
to this
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
}
DataRow row = table.NewRow();
else if (isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
}
it should work just fine. By only creating a row if the column headers have been created you're creating a situation wherein only on the first pass do you do anything with the first row, then it gets dumped.
I would think you want to add the columns or add a row.
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
}
}
else
{
DataRow row = table.NewRow();
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
}
This would work
DataTable dt = new DataTable();
using (System.IO.StreamReader sr = new System.IO.StreamReader("PathToFile"))
{
string currentline = string.Empty;
bool doneHeader = false;
while ((currentline = sr.ReadLine()) != null)
{
if (!doneHeader)
{
foreach (string item in currentline.Split('YourDelimiter'))
{
dt.Columns.Add(item);
}
doneHeader = true;
continue;
}
dt.Rows.Add();
int colCount = 0;
foreach (string item in currentline.Split('YourDelimiter'))
{
dt.Rows[dt.Rows.Count - 1][colCount] = item;
colCount++;
}
}
}
Another method, more LINQ oriented.
Use File.ReadAllLines to parse all the File lines into a string array.
Create a List<string[]> containing all the data Rows. The columns values are composed splitting the string row using a
provided Delimiter.
The first Row values are used to build a DataTable Columns elements.
The first Row is removed from the List.
All the other Rows are added to the DataTable.Rows collection.
Set a DataGridView.DataSource to the new DataTable.
char Delimiter = '|';
string[] Lines = File.ReadAllLines("[SomeFilePath]", Encoding.Default);
List<string[]> FileRows = Lines.Select(line =>
line.Split(new[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries)).ToList();
DataTable dt = new DataTable();
dt.Columns.AddRange(FileRows[0].Select(col => new DataColumn() { ColumnName = col }).ToArray());
FileRows.RemoveAt(0);
FileRows.ForEach(row => dt.Rows.Add(row));
dataGridView1.DataSource = dt;

How to Check for Empty data entry before populating ListBox

private void LoadExcelSheet(string path, int sheet){
_Application excel = new Excel.Application();
Workbook wb;
Worksheet ws;
string data = "";
int row = 0;
int col = 0;
wb = excel.Workbooks.Open(path);
ws = wb.Worksheets[sheet];
listBox1.Items.Clear();
for (row = 1; row < 10; row++){
data = " ";
for (col = 1; col < 3; col++) {
data += ws.Cells[row, col].Value2 + " ";
}
//wanted to filter out empty cells/data and at the same time count
//number of items in the list... row should stop.. I think!
if(data == null){
break;
}
listBox1.Items.Add(data);
}
The if statement doesn't seems to work no matter what I do. I would appreciate it very if anyone could point me in the right direction.
use it like this:
if (data.Trim().Length < 1)
{
return;
}
use return not break
Add a condition like this
If(string.IsNullOrEmpty(data))
{
Break;
}

How can I paste multi-rows from Excel to a DataGridView in C#?

I am trying to paste rows from an Excel sheet to a DataGridView in C#.
I have used the following code:
private void PasteClipboard(DataGridView myDataGridView)
{
DataObject o = (DataObject)Clipboard.GetDataObject();
if (o.GetDataPresent(DataFormats.Text))
{
if (myDataGridView.RowCount > 0)
myDataGridView.Rows.Clear();
if (myDataGridView.ColumnCount > 0)
myDataGridView.Columns.Clear();
bool columnsAdded = false;
string[] pastedRows = Regex.Split(o.GetData(DataFormats.Text).ToString().TrimEnd("\r\n".ToCharArray()), "\r\n");
foreach (string pastedRow in pastedRows)
{
string[] pastedRowCells = pastedRow.Split(new char[] { '\t' });
if (!columnsAdded)
{
for (int i = 0; i < pastedRowCells.Length; i++)
myDataGridView.Columns.Add("col" + i, pastedRowCells[i]);
columnsAdded = true;
continue;
}
myDataGridView.Rows.Add();
int myRowIndex = myDataGridView.Rows.Count - 1;
using (DataGridViewRow myDataGridViewRow = myDataGridView.Rows[myRowIndex])
{
for (int i = 0; i < pastedRowCells.Length; i++)
myDataGridViewRow.Cells[i].Value = pastedRowCells[i];
}
}
However, as a result, only one row contains data while the others are empty. For instance, if I copy and paste 3 rows, the 3rd row is the only row with data and the other two rows are empty. What am I doing wrong?
You need to do this:
int myRowIndex = myDataGridView.Rows.Add();
Instead of this:
myDataGridView.Rows.Add();
int myRowIndex = myDataGridView.Rows.Count - 1;
Note that when you create a new row, you also receive the index of that row, as the return value of myDataGridView.Rows.Add(); . Your code ignores that value and instead it assumes that the newly created row will always be the last one: myDataGridView.Rows.Count - 1;

SSIS Export to Excel using Script Task

I'm trying to use a Script Task to export data to Excel because some of the reports I generate simply have too many columns to keep using a template file.
The most annoying part about using a template is: if something as simple as a column header changes, the metadata gets screwed forcing me to recreate my DataFlow. Because I use an OLE DB source, I need to use a Data Transformation task to convert between unicode and non-unicode character sets, then remap my Excel Destination to the "Copy of field x" in order for the Excel document to create properly.
This takes far too long and I need a new approach.
I have the following method in a script task using Excel = Microsoft.Office.Interop.Excel:
private void ExportToExcel(DataTable dataTable, string excelFilePath = null)
{
Excel.Application excelApp = new Excel.Application();
Excel.Worksheet workSheet = null;
try
{
if (dataTable == null || dataTable.Columns.Count == 0)
throw new System.Exception("Null or empty input table!" + Environment.NewLine);
excelApp.Workbooks.Add();
workSheet = excelApp.ActiveSheet;
for (int i = 0; i < dataTable.Columns.Count; i++)
{
workSheet.Cells[1, (i + 1)] = dataTable.Columns[i].ColumnName;
}
foreach (DataTable dt in dataSet.Tables)
{
// Copy the DataTable to an object array
object[,] rawData = new object[dt.Rows.Count + 1, dt.Columns.Count];
// Copy the column names to the first row of the object array
for (int col = 0; col < dt.Columns.Count; col++)
{
rawData[0, col] = dt.Columns[col].ColumnName;
}
// Copy the values to the object array
for (int col = 0; col < dt.Columns.Count; col++)
{
for (int row = 0; row < dt.Rows.Count; row++)
{
rawData[row + 1, col] = dt.Rows[row].ItemArray[col];
}
}
// Calculate the final column letter
string finalColLetter = string.Empty;
string colCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int colCharsetLen = colCharset.Length;
if (dt.Columns.Count > colCharsetLen)
{
finalColLetter = colCharset.Substring((dt.Columns.Count - 1) / colCharsetLen - 1, 1);
}
finalColLetter += colCharset.Substring((dt.Columns.Count - 1) % colCharsetLen, 1);
workSheet.Name = dt.TableName;
// Fast data export to Excel
string excelRange = string.Format("A1:{0}{1}", finalColLetter, dt.Rows.Count + 1);
//The code crashes here (ONLY in SSIS):
workSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
// Mark the first row as BOLD
((Excel.Range)workSheet.Rows[1, Type.Missing]).Font.Bold = true;
}
List<int> lstColumnsToSum = new List<int>() { 9 };
Dictionary<int, string> dictColSumName = new Dictionary<int, string>() { { 9, "" } };
Dictionary<int, decimal> dictColumnSummation = new Dictionary<int, decimal>() { { 9, 0 } };
// rows
for (int i = 0; i < dataTable.Rows.Count; i++)
{
for (int j = 1; j <= dataTable.Columns.Count; j++)
{
workSheet.Cells[(i + 2), (j)] = dataTable.Rows[i][j - 1];
if (lstColumnsToSum.Exists(x => (x == j)))
{
decimal val = 0;
if (decimal.TryParse(dataTable.Rows[i][j - 1].ToString(), out val))
{
dictColumnSummation[j] += val;
}
}
}
}
//Footer
int footerRowIdx = 2 + dataTable.Rows.Count;
foreach (var summablecolumn in dictColSumName)
{
workSheet.Cells[footerRowIdx, summablecolumn.Key] = String.Format("{0}", dictColumnSummation[summablecolumn.Key]);
}
// check fielpath
if (excelFilePath != null && excelFilePath != "")
{
try
{
if (File.Exists(excelFilePath))
File.Delete(excelFilePath);
workSheet.Activate();
workSheet.Application.ActiveWindow.SplitRow = 1;
workSheet.Application.ActiveWindow.FreezePanes = true;
int row = 1;
int column = 1;
foreach (var item in dataTable.Columns)
{
Excel.Range range = workSheet.Cells[row, column] as Excel.Range;
range.NumberFormat = "#";
range.EntireColumn.AutoFit();
range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
column++;
}
Excel.Range InternalCalculatedAmount = workSheet.Cells[1, 9] as Excel.Range;
InternalCalculatedAmount.EntireColumn.NumberFormat = "#0.00";
InternalCalculatedAmount.Columns.AutoFit();
workSheet.SaveAs(excelFilePath);
}
catch (System.Exception ex)
{
throw new System.Exception("Excel file could not be saved! Check filepath." + Environment.NewLine + ex.Message);
}
}
else // no filepath is given
{
excelApp.Visible = true;
}
}
catch (System.Exception ex)
{
throw new System.Exception("ex.Message + Environment.NewLine, ex.InnerException);
}
}
The exception thrown is a System.OutOfMemoryException when trying to execute the following piece of code:
workSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
My biggest frustration is that this method works 100% in a regular C# application.
The DataTable contains about 435000 rows. I know it's quite a bit of data but I use this very method, modified of course, to split data across multiple Excel worksheets in one of my other applications, and that DataSet contains about 1.1m rows. So less than half of my largest DataSet should be a walk-in-the-park...
Any light shed on this matter would be amazing!

c# Excelpackage reading cell range into a datatable

Using C# I can manage to read in range of cells using Excel interop. But it's having trouble keeping consistent with dollar values and percentages. So I'm trying out EEPLus.
FileInfo newFile = new FileInfo(#excelFilePath);
ExcelPackage pck = new ExcelPackage(newFile);
var Summary = workbook1.Worksheets[1];
and then using the following
Convert.ToString(Summary.Cells["I35"].Value);
I get a decent value.
But what I would like to do is something the following
Summary.Cells["E29:P32"].Value
and put that into a datatable. There is a method for ImportDataTable but it moves from datatable to excel. Does something similarly simple exist for excel to datatable?
Cheers.
There is no built in method, but you can try something like this:
var range = summary.Cells["E6:G7"];
DataTable tbl = GetDataTableFromRange(range);
and GetDataTableFromRange method:
public static DataTable GetDataTableFromRange(ExcelRange range)
{
DataTable tbl = new DataTable();
tbl.Columns.Add("Column1");
tbl.Columns.Add("Column2");
tbl.Columns.Add("Column3");
int dataTableColumn = 0;
DataRow newRow = null;
int currRow = -1;
foreach (var item in range)
{
if (currRow != item.Start.Row)
{
newRow = tbl.NewRow();
tbl.Rows.Add(newRow);
dataTableColumn = 0;
currRow = item.Start.Row;
}
newRow[dataTableColumn] = item.Value.ToString();
dataTableColumn++;
}
return tbl;
}
You can do like this:
public bool readXLS(string FilePath)
{
FileInfo existingFile = new FileInfo(FilePath);
using (ExcelPackage package = new ExcelPackage(existingFile))
{
//get the first worksheet in the workbook
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
int colCount = worksheet.Dimension.End.Column; //get Column Count
int rowCount = worksheet.Dimension.End.Row; //get row count
string queryString = "INSERT INTO tableName VALUES"; //Here I am using "blind insert". You can specify the column names Blient inset is strongly not recommanded
string eachVal = "";
bool status;
for (int row = 1; row <= rowCount; row++)
{
queryString += "(";
for (int col = 1; col <= colCount; col++)
{
eachVal = worksheet.Cells[row, col].Value.ToString().Trim();
queryString += "'" + eachVal + "',";
}
queryString = queryString.Remove(queryString.Length - 1, 1); //removing last comma (,) from the string
if (row % 1000 == 0) //On every 1000 query will execute, as maximum of 1000 will be executed at a time.
{
queryString += ")";
status = this.runQuery(queryString); //executing query
if (status == false)
return status;
queryString = "INSERT INTO tableName VALUES";
}
else
{
queryString += "),";
}
}
queryString = queryString.Remove(queryString.Length - 1, 1); //removing last comma (,) from the string
status = this.runQuery(queryString); //executing query
return status;
}
}
Source: http://sforsuresh.in/read-data-excel-sheet-insert-database-table-c/
I'ill do like this to find the range and store the values in database.
bool hasHeader = true;
using (var pck = new OfficeOpenXml.ExcelPackage(file.InputStream))
{
var ws = pck.Workbook.Worksheets.First();
DataTable tbl = new DataTable();
foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
{
if (firstRowCell.Text != "" && firstRowCell.Text != null)
{
tbl.Columns.Add(hasHeader ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
}
else
{
break;
}
}
var startRow = hasHeader ? 2 : 1;
for (int rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
{
var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
DataRow row = tbl.Rows.Add();
foreach (var cell in wsRow)
{
if (cell.Text != "" && cell.Text != null)
row[cell.Start.Column - 1] = cell.Text;
}
}
}

Categories