C# Split String to Excel - c#

I have been reading up on Split string. I am sending data from C# to Excel and some of this text can be rather long. So without using word wrap or autofit with Excel. I would like the data to break at a certain point and continue to the row below it.Can this be accomplished? ProdDescription is the targeted field here. Here is the code I am using to send the basic data over :
worksheet.Cells[3, "E"].Value = txtCustomer.Text;
worksheet.Cells[4, "E"].Value = txtDate.Text;
worksheet.Cells[5, "E"].Value = cboTerms.Text;
worksheet.Cells[6, "E"].Value = txtProposalID.Text;
worksheet.Cells[10, "D"].value = frmProposal.QtyMaintxt.Text;
worksheet.Cells[10, "E"].value = frmProposal.ProdName.Text;
worksheet.Cells[11, "E"].value = frmProposal.ProdDescription.Text;**
worksheet.Cells[10, "F"].value = frmProposal.ListPrice.Text;
worksheet.Cells[10, "G"].value = frmProposal.MaxDiscount.Text;

Try it like this:
string s = "This is a rather long text. This is a rather long text. This is a rather long text. This is a rather long text. This is a rather long text. This is a rather long text. ";
s += "This is a rather long text. This is a rather long text. This is a rather long text. This is a rather long text. This is a rather long text. This is a rather long text. ";
s += "This is a rather long text. This is a rather long text. This is a rather long text. This is a rather long text. This is a rather long text. This is a rather long text. ";
var words = s.Split(new char[] { ' ' });
int maxLineCount = 35;
var sb=new System.Text.StringBuilder();
string part=words[0];
int i=1;
while (true) {
if (i >= words.Length)
break;
if ((part + " " + words[i]).Length < maxLineCount)
part += " " + words[i];
else {
sb.AppendLine(part);
part = words[i];
}
i++;
}
var result = sb.ToString();
You could write the generated "lines" into an array or directly into your cells too. I just used Stringbuilder to check the result easily...

Related

Calculating richtextbox lines count asynchronously

I write a code that counts the number of lines and text length from richtextbox content. With small chunks of text it work perfect. But when there large chunks of text (more than 100k) when I press "Enter" or "Backspace" in richtextbox, response time becomes very slow. For example: https://i.imgur.com/QO2UrAw.gifv
My question. What a better way to run this code asynchronously?
Archive with the test project https://gofile.io/?c=LpF409
private void StatusPanelTextInfo()
{
int currentColumn = 0;
int currentLine = 0;
int linesCount = 0;
if (statusStrip1.Visible)
{
currentColumn = 1 + richTextBox1.SelectionStart - richTextBox1.GetFirstCharIndexOfCurrentLine();
RichTextBox rtb = new RichTextBox
{
WordWrap = false,
Text = richTextBox1.Text
};
currentLine = 1 + rtb.GetLineFromCharIndex(richTextBox1.SelectionStart);
linesCount = richTextBox1.Lines.Count();
if (linesCount == 0)
{
linesCount = 1;
}
}
toolStripStatusLabel1.Text = "Length: " + richTextBox1.TextLength;
toolStripStatusLabel2.Text = "Lines: " + linesCount;
toolStripStatusLabel3.Text = "Ln: " + currentLine;
toolStripStatusLabel4.Text = "Col: " + currentColumn;
}
I downloaded your code and I can not understand why do you create a new RichTextBox every time you call StatusPanelTextInfo method:
RichTextBox rtb = new RichTextBox
{
WordWrap = false,
Text = richTextBox1.Text
};
This is the reason you got such a lag in your program. Each time you change/select text, you create a new RichTextBox object and copy a large amount of text to its Text property. You should remove this code, and then it works fast enough. Just replace rtb in your calculation of currentLine with richTextBox1.
Next time please provide your code in your question instead of making people download it from outer link. Your whole form class was about 60 lines. With proper selection you could have given us all the info we needed using 20 lines.

Split a text in blocks of text when encountering a defalult sign

So, I have been working with some texts.
I've trying to separate the text in multiple blocks when I encountered '$'sign in text. In my example I used two richTextBox. I tried using lists, and Split() method but it didn't work so well.
StreamReader read = new StreamReader(#"texte\Senzatii\definirea&caracterizarea_senzatiilor.txt");
string lines = "";
int state = 1;
while ((lines = read.ReadLine()) != null)
{
if (lines == "$".ToString())
state = 2;
if (state == 1)
richTextBox1.Text = richTextBox1.Text + lines + "\n";
else
richTextBox2.Text = richTextBox2.Text + lines + "\n";
}
This method works, it splits the text into two block of texts, but it doesn't look so good. Is there a better way to split the text into two blocks of text, a more c# way, solution to do this ?
I tried using lists, and Split() method but it didn't work so well.
Hard to help with almost no informations. But if you want to split on this $ sign. What's wrong with:
string[] bothParts = File.ReadAllText(#"texte\Senzatii\definirea&caracterizarea_senzatiilor.txt")
.Split('$');
string firstPart = bothParts[0];
string secondPart = bothParts.ElementAtOrDefault(1);
richTextBox1.Text = firstPart;
richTextBox2.Text = secondPart;

Text reformatter gradually slows with each iteration

EDIT 2
Okay, I've posted a copy of my source at gist.github and I only have one lingering problem that I can't resolve.
FindLine() always returns -1. I've narrowed the cause down to the if statement, but I can't figure out why. I know that symbol and symbolList are both getting passed good data.
/EDIT 2
I have a fairly simple C# program that looks for a .csv file, reads the text in that file, reformats it (and includes some information from a SQL query loaded into a DataTable), and saves it to a .tsv file for later use by another program.
My problem is that sometimes the source .csv file is over 10,000 lines and the program slows gradually as it iterates through the lines. If the .csv file is ~500 lines it takes about 45 seconds to complete, and this time get exponentially worse as the .csv file gets larger.
The SQL query returns 37,000+ lines, but is only requested once and is sorted the same way the .csv file is, so normally I won't notice it running through that file unless it can't find the corresponding data, in which case it makes it all the way through and returns the appropriate error text. I'm 99% sure it's not the cause of the slowdown.
The y and z for loops need to be exactly as long as they are.
If it's absolutely necessary I can scrub some data from the initial .csv file and post an example, but I'm really hoping I'm just missing something really obvious.
Thanks in advance guys!
Here's my source:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
namespace MoxySectorFormatter
{
class Program
{
static void Main(string[] args)
{
DataTable resultTable = new DataTable();
double curLine = 1;
double numLines = 0;
string ExportPath = #"***PATH***\***OUTFILE***.tsv";
string ImportPath = #"***PATH***\***INFILE***.csv";
string NewText = "SECURITY\r\n";
string OrigText = "";
string QueryString = "SELECT DISTINCT UPPER(MP.Symbol) AS Symbol, LOWER(MP.SecType) AS SecType, MBI.Status FROM MoxySecMaster AS MP LEFT JOIN MoxyBondInfo AS MBI ON MP.Symbol = MBI.Symbol AND MP.SecType = MBI.SecType WHERE MP.SecType <> 'caus' AND MP.SecType IS NOT NULL AND MP.Symbol IS NOT NULL ORDER BY Symbol ASC;";
SqlConnection MoxyConn = new SqlConnection("server=***;database=***;user id=***;password=***");
SqlDataAdapter adapter = new SqlDataAdapter(QueryString, MoxyConn);
MoxyConn.Open();
Console.Write("Importing source file from \"{0}\".", ImportPath);
OrigText = File.ReadAllText(ImportPath);
OrigText = OrigText.Substring(OrigText.IndexOf("\r\n", 0) + 2);
Console.WriteLine("\rImporting source file from \"{0}\". Done!", ImportPath);
Console.Write("Scanning source report.");
for (int loop = 0; loop < OrigText.Length; loop++)
{
if (OrigText[loop] == '\r')
numLines++;
}
Console.WriteLine("\rScanning source report. Done!");
Console.Write("Downloading SecType information.");
resultTable = new DataTable();
adapter.Fill(resultTable);
MoxyConn.Close();
Console.WriteLine("\rDownloading SecType information. Done!");
for (int lcv = 0; lcv < numLines; lcv++)
{
int foundSpot = -1;
int nextStart = 0;
Console.Write("\rGenerating new file... {0} / {1} ({2}%) ", curLine, numLines, System.Math.Round(((curLine / numLines) * 100), 2));
for (int vcl = 0; vcl < resultTable.Rows.Count; vcl++)
{
if (resultTable.Rows[vcl][0].ToString() == OrigText.Substring(0, OrigText.IndexOf(",", 0)).ToUpper() && resultTable.Rows[vcl][1].ToString().Length > 0)
{
foundSpot = vcl;
break;
}
}
if (foundSpot != -1 && foundSpot < resultTable.Rows.Count)
{
NewText += resultTable.Rows[foundSpot][1].ToString();
NewText += "\t";
NewText += OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart));
NewText += "\t";
nextStart = OrigText.IndexOf(",", nextStart) + 1;
for (int y = 0; y < 142; y++)
NewText += "\t";
if(resultTable.Rows[foundSpot][2].ToString() == "r")
NewText += #"PRE/ETM";
else if (OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart)) == "Municipals")
{
NewText += "Muni - ";
nextStart = OrigText.IndexOf(",", nextStart) + 1;
if (OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart)).Length > 0)
NewText += OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart));
else
NewText += "(Orphan)";
}
else if (OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart)) == "Corporates")
{
NewText += "Corporate - ";
nextStart = OrigText.IndexOf(",", nextStart) + 1;
nextStart = OrigText.IndexOf(",", nextStart) + 1;
if (OrigText.Substring(nextStart, (OrigText.IndexOf("\r\n", nextStart) - nextStart)).Length > 0)
NewText += OrigText.Substring(nextStart, (OrigText.IndexOf("\r\n", nextStart) - nextStart));
else
NewText += "(Unknown)";
}
else
NewText += OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart));
for (int z = 0; z < 17; z++)
NewText += "\t";
NewText += "\r\n";
resultTable.Rows.RemoveAt(foundSpot);
}
else
Console.WriteLine("\r Omitting {0}: Missing Symbol or SecType.", OrigText.Substring(nextStart, (OrigText.IndexOf(",", nextStart) - nextStart)));
OrigText = OrigText.Substring(OrigText.IndexOf("\r\n", 0) + 2);
curLine++;
}
Console.Write("Exporting file to \"{0}\".", ExportPath);
File.WriteAllText(ExportPath, NewText);
Console.WriteLine("\rExporting file to \"{0}\". Done!\nPress any key to exit.", ExportPath);
Console.ReadLine();
}
}
}
Instead of using the += operator for concatenation, use a System.Text.StringBuilder object and it's Append() and AppendLine Methods
Strings are immutable in C#, so every time you use += in your loop, a new string is created in memory, likely causing the eventual slowdown.
While this cries out for StringBuilder I don't believe that's the primary culprit here. Rather, we have a piece of code with an exponential runtime.
The culprit I have in mind is the code that calculates foundSpot. If I'm reading the code correctly this is O(n^2) while everything else is O(n).
Three pieces of advise:
1) Refactor! This routine is WAY too long. I shouldn't have to refer to "the code that calculates foundSpot", that should be a routine with a name. I see a minimum of 4 routines here, maybe more.
2) Stringbuilder.
3) That search routine has to be cleaned up. You're doing a lot of repeated calculations each time around the loop and unless there's some reason against it (I'm not going to try to figure out the tests you are applying) it needs to be done with something with search performance better than O(n).
You should write each line to the output file as you create it instead of appending all lines to the end of your output string (NewText) and writing them out at the end.
Every time that the program appends something to the end of the output string, C# creates a new string that's big enough for the old string plus the appended text, copies the old contents into the target string, then appends the new text to the end.
Assuming 40 characters per line and 500 lines, the total string size will be ~ 20K, at which point the overhead of all of those 20K copies is slowing the program WAY down.
NewText is only appended to, right? So why not just write out to the file stream? Also don't forget to add a try catch around it, so if your app blows up, you can close the file stream.
Also, the second loop would probably be faster if you pulled out the SubString call. There is no reason to be doing that over and over again.
string txt = OrigText.Substring(0, OrigText.IndexOf(",", 0)).ToUpper()
for (int vcl = 0; vcl < resultTable.Rows.Count; vcl++)
{
if (resultTable.Rows[vcl][0].ToString() == txt && resultTable.Rows[vcl][1].ToString().Length > 0)
{
foundSpot = vcl;
break;
}
}
These tab loops are ridiculous. They are essentially constant strings that get built each time. Replace them with formatting vars that are declared at the start of your app.
string tab17 = "\t\t\t\t\t\t\t\t\t"
string tab142 = "\t\t\t\t\t...etc."
//bad
for (int z = 0; z < 17; z++)
NewText += "\t";

Catch null values before adding to a string

Hey guys, I can only apologise for asking such a noob question, but this is the second time today I'm going to be relying on this awesome forum :)
I have a string that is made up of values from an array, taken from an excel sheet, only some of these values are blank. I'm outputting these values to a CSV file, but don't want the blank values to output, these values are outputting as commas, and so are giving me extra lines in my CSV file.
Anyway, here's my code, I want to compare the array values separately to not equal "" before I add them to the string. If this is not possible, I am thinking that I could split the string, compare, and then rejoin it.
enter code here//Get a range of data.
range = objSheet.get_Range(ranges1, ranges2);
//Retrieve the data from the range.
Object[,] saRet;
saRet = (System.Object[,])range.get_Value(Missing.Value);
//Determine the dimensions of the array.
long iRows;
long iCols;
iRows = saRet.GetUpperBound(0);
iCols = saRet.GetUpperBound(1);
//Build a string that contains the data of the array.
String valueString;
valueString = "";
System.IO.StreamWriter OutWrite = new System.IO.StreamWriter("h:\\out.csv");
for (long rowCounter = 1; rowCounter <= iRows; rowCounter++)
{
for (long colCounter = 1; colCounter <= iCols; colCounter++)
{
//Write the next value into the string.
valueString = String.Concat(valueString, Convert.ToString(saRet[rowCounter, colCounter]) + ", ");
}
//Write in a new line.
{
valueString = String.Concat(valueString, "\n");
}
}
//Report the value of the array.
MessageBox.Show(valueString, "Array Values");
OutWrite.WriteLine(valueString);
OutWrite.Close();
OutWrite.Dispose();
}
Cheers, and thanks for your patience!
Andy.
I think you are looking for String.IsNullOrEmpty Method .
(Or String.IsNullOrWhiteSpace Method in .Net 4)
public static class ExtensionLib
{
public static string MyConcat(this string input, params string[] others)
{
if (others == null || others.Length == 0)
return input;
string retVal = input??"";
foreach(string str in others)
{
if (String.IsNullOrEmpty(str))
return input;
retVal += str;
}
return retVal;
}
}
and use it as bellow:
....
valueString.MyConcat(arg1, arg2, ...);
Take a care about this line:
valueString = String.Concat(valueString, Convert.ToString(saRet[rowCounter, colCounter]) + ", ");
instead write
valueString = valueString.MyConcat(Convert.ToString(saRet[rowCounter, colCounter]), ", ");
String valueString;
valueString = "";
if (!valueString.Equals(compare))
You can just rewrite this as:
if (!String.IsNullOrEmpty(compare))
In fact, you should. No need to reinvent what .NET provides.
newValue += (oldValue ?? string.empty)

c# Optimized Select string creation loop

I'm looking to optimized this piece of code. It will process 15000 - 20000 lines. For now I have 9000 lines and it take 30 sec approx. I know string concatenation is slow but I don't know how to do it another way.
//
// Check if composite primary keys existe in database
//
string strSelect = "SELECT * FROM " + _strTableName + " WHERE ";
for (int i = 0; i < strCompositeKeyField.Length; i++)
{
bool boolKeyProcess = false;
strSelect += _strHeaderLineSplitedArray[(int)arrayListCompositeKeyIndex[i]] + " = ";
DataColumn thisColomn = _dsProcessDataFromFileAndPutInDataSetDataSet.Tables["Repartition"].Columns[_strHeaderLineSplitedArray[(int)arrayListCompositeKeyIndex[i]]];
//_strProcessDataFromFileAndPutInDataSetLog += "Debug: Composite key : " + _strHeaderLineSplitedArray[(int)arrayListCompositeKeyIndex[i]] + " dataType : " + thisColomn.DataType.ToString() + " arrayListCompositeKeyIndex[i] = " + arrayListCompositeKeyIndex[i] + " \n";
// check if field is datetime to make convertion
if (thisColomn.DataType.ToString() == "System.DateTime")
{
DateTime thisDateTime = DateTime.ParseExact(strReadDataLineSplited[(int)arrayListCompositeKeyIndex[i]], _strDateConvertion, null);
strSelect += "'" + thisDateTime.ToString() + "'";
boolKeyProcess = true;
}
// check if field a string to add ''
else if (thisColomn.DataType.ToString() == "System.String")
{
strSelect += "'" + strReadDataLineSplited[(int)arrayListCompositeKeyIndex[i]] + "'";
boolKeyProcess = true;
}
// check if field need hour to second converstion
else
{
for (int j = 0; j < strHourToSecondConverstionField.Length; j++)
{
if (strCompositeKeyField[i] == strHourToSecondConverstionField[j])
{
DateTime thisDateTime = DateTime.ParseExact(strReadDataLineSplited[(int)arrayListCompositeKeyIndex[i]], _strHourConvertion, System.Globalization.CultureInfo.CurrentCulture);
strSelect += thisDateTime.TimeOfDay.TotalSeconds.ToString();
boolKeyProcess = true;
}
}
}
// if not allready process process as normal
if (!boolKeyProcess)
{
strSelect += strReadDataLineSplited[(int)arrayListCompositeKeyIndex[i]];
}
// Add " AND " if not last field
if (i != strCompositeKeyField.Length - 1)
{
strSelect += " AND ";
}
}
//_strProcessDataFromFileAndPutInDataSetLog += "Debug: SELECT = " + strSelect + "\n";
SqlDataAdapter AdapterCheckCompositePrimaryKeys = new SqlDataAdapter(strSelect, _scProcessDataFrinFileAndPutInDataSetSqlConnection);
DataSet DataSetCheckCompositePrimaryKeys = new DataSet();
AdapterCheckCompositePrimaryKeys.Fill(DataSetCheckCompositePrimaryKeys, "PrimaryKey");
You should definitely take a look at StringBuilder - it works wonders for scenarios like this one. In this case, i'd use a mix of AppendFormat and Append. I tend to like AppendFormat to make the strings a bit easier to follow.
//
// Check if composite primary keys existe in database
//
StringBuilder strSelect = "SELECT * FROM " + _strTableName + " WHERE ";
for (int i = 0; i < strCompositeKeyField.Length; i++)
{
bool boolKeyProcess = false;
strSelect.AppendFormat("{0} =",
_strHeaderLineSplitedArray[(int)arrayListCompositeKeyIndex[i]]);
DataColumn thisColomn =
_dsProcessDataFromFileAndPutInDataSetDataSet
.Tables["Repartition"]
.Columns[_strHeaderLineSplitedArray[(int)arrayListCompositeKeyIndex[i]]];
//_strProcessDataFromFileAndPutInDataSetLog += "Debug: Composite key : " + _strHeaderLineSplitedArray[(int)arrayListCompositeKeyIndex[i]] + " dataType : " + thisColomn.DataType.ToString() + " arrayListCompositeKeyIndex[i] = " + arrayListCompositeKeyIndex[i] + " \n";
// check if field is datetime to make convertion
if (thisColomn.DataType.ToString() == "System.DateTime")
{
DateTime thisDateTime =
DateTime.ParseExact(strReadDataLineSplited[(int)arrayListCompositeKeyIndex[i]],
_strDateConvertion, null);
strSelect.AppendFormat("'{0}'", thisDateTime.ToString());
boolKeyProcess = true;
}
// check if field a string to add ''
else if (thisColomn.DataType.ToString() == "System.String")
{
strSelect.AppendFormat("'{0}'",
strReadDataLineSplited[(int)arrayListCompositeKeyIndex[i]]);
boolKeyProcess = true;
}
// check if field need hour to second converstion
else
{
for (int j = 0; j < strHourToSecondConverstionField.Length; j++)
{
if (strCompositeKeyField[i] == strHourToSecondConverstionField[j])
{
DateTime thisDateTime = DateTime.ParseExact(
strReadDataLineSplited[(int)arrayListCompositeKeyIndex[i]],
_strHourConvertion,
System.Globalization.CultureInfo.CurrentCulture);
strSelect.Append(thisDateTime.TimeOfDay.TotalSeconds.ToString());
boolKeyProcess = true;
}
}
}
// if not allready process process as normal
if (!boolKeyProcess)
{
strSelect.Append(strReadDataLineSplited[(int)arrayListCompositeKeyIndex[i]]);
}
// Add " AND " if not last field
if (i != strCompositeKeyField.Length - 1)
{
strSelect.Append(" AND ");
}
}
//_strProcessDataFromFileAndPutInDataSetLog += "Debug: SELECT = " + strSelect + "\n";
SqlDataAdapter AdapterCheckCompositePrimaryKeys = new SqlDataAdapter(strSelect.ToString(), _scProcessDataFrinFileAndPutInDataSetSqlConnection);
DataSet DataSetCheckCompositePrimaryKeys = new DataSet();
AdapterCheckCompositePrimaryKeys.Fill(DataSetCheckCompositePrimaryKeys, "PrimaryKey");
Use a StringBuilder and its Append() method.
Make use of a StringBuilder rather than string concatenation.
Use StringBuilder for your string manipulation like strSelect += ...
instead use stringBuilder.Append("...");
Have u tried the StringBuilder object ?
http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx
To answer your direct question you'll almost certainly benefit from using StringBuilder to build the string up, then ToString() it at the end.
However, if you could give us an overview of the intention (so we don't have to wade through to deduce it) we can probably recommend a better way.
After a quick look, one thing that stands out is that you should be using the StringBuilder class to build up the string, instead of continually concatenating on to your strSelect string variable. Excerpt from the linked MSDN article:
The performance of a concatenation operation for a String or
StringBuilder object depends on how
often a memory allocation occurs. A
String concatenation operation always
allocates memory, whereas a
StringBuilder concatenation operation
only allocates memory if the
StringBuilder object buffer is too
small to accommodate the new data.
Consequently, the String class is
preferable for a concatenation
operation if a fixed number of String
objects are concatenated. In that
case, the individual concatenation
operations might even be combined into
a single operation by the compiler. A
StringBuilder object is preferable for
a concatenation operation if an
arbitrary number of strings are
concatenated; for example, if a loop
concatenates a random number of
strings of user input.
I'm a database guy, so hopefully I don't sound like an idiot here, but can you use the StringBuilder class? I don't know if that requires the .NET framework or not.
You've received a lot of good suggestions to use StringBuilder. To improve your performance more and since you are expecting a large string result, I would recommend as well, that you choose a good initial capacity to reduce the number of times the internal string buffer needs to be expanded.
StringBuilder strSelect = new StringBuilder("SELECT * FROM " + _strTableName + " WHERE ", 8192);
Note that I picked 8192 characters here, but you may want to initialize this to a larger number if you really are adding "9000" lines of data to your condition. Find out your typical size and set it the capacity to just a small amount above that.
The way StringBuilder works is that as you append to the string and reach the current capacity, it must create a new buffer and copy the contents of the old buffer to the new one then append the new characters. To optimize performance, the new buffer will be double the old size. Now, the default initial capacity is either 16 characters or the size of the initializing string. If your resulting string is 5000 characters long, then a StringBuilder created with a default size will have to be expanded 9 times - that requires new memory allocation and copying all the previous characters! If, however, you knew this would happen regularly and created the StringBuilder with the right capacity, there would be no additional allocation or copies.
Normally you should not need to worry about internal operations of an object, but you do sometimes for performance such as this. Since StringBuilder does provide a way for you to specific a recommended initial capacity, take advantage of that. You do not know if the doubly/copy algorithm will change in the future nor the initial default capacity may change, but your specification of an initial capacity will continue to allocate the right sized builder - because this is part of the public contract.

Categories