Split string by length to Excel - c#

I am currently sending and splitting long lines of data to Excel. Each split prints in a new row. I want to change this from splitting at a pipe symbol in the existing data to splitting at a character length of 85, but , there are chances that at character 85 it may split a word in two. How would I tell it to split further into the data if is going to split an actual word. I know if at 85 it should also find a space after. I'm curious on what to add.
// Add Description
string DescriptionSplit = srcAddOnPanel.Controls["txtProductDescAddOn" + AddRow].Text;
string[] descriptionParts = DescriptionSplit.Split('|');
int i;
for (i = 0; i <= descriptionParts.GetUpperBound(0); i++)
{
worksheet.Rows[currentRow].Insert(); //applies the description for the default bundle row
worksheet.Rows[currentRow].Font.Bold = false;
worksheet.Cells[currentRow, "E"].Value = rowIndent + descriptionParts[i].Trim();
currentRow++;
}

You could use this approach (Warning not fully tested)
int x = 85;
int y = 0;
int currentRow = 0;
// Loop until you have at least 85 char to grab
while (x + y < DescriptionSplit.Length)
{
// Find the first white space after the 85th char
while (x + y < DescriptionSplit.Length &&
!char.IsWhiteSpace(DescriptionSplit[x+y]))
x++;
// Grab the substring and pass it to Excel for the currentRow
InsertRowToExcel(DescriptionSplit.Substring(y, x), currentRow);
// Prepare variables for the next loop
currentRow++;
y = y + x + 1;
x = 85;
}
// Do not forget the last block
if(y < DescriptionSplit.Length)
InsertRowToExcel(DescriptionSplit.Substring(y), currentRow);
...
void InsertRowToExcel(string toInsert, int currentRow)
{
worksheet.Rows[currentRow].Insert();
worksheet.Rows[currentRow].Font.Bold = false;
worksheet.Cells[currentRow, "E"].Value = rowIndent + toInsert.Trim();
}

Here's a VBA version that seems to work. As suggested in my comment, it separates the string by spaces and then tests whether adding a word makes the length of the current line greater than the maximum (85). As is usual with these kinds of things getting the last word to populate correctly is hard.
If this works for you it should be simple enough to modify to C#. Let me know if that's not true:
Sub ParseRows()
Const ROW_LENGTH As Long = 85
Dim Text As String
Dim Words As Variant
Dim RowContent As String
Dim RowNum As Long
Dim i As Long
Text = ActiveCell.Text
Words = Split(Text, " ")
RowNum = 2
For i = LBound(Words) To UBound(Words)
If Len(RowContent & Words(i) & " ") > ROW_LENGTH Then
ActiveSheet.Range("A" & RowNum).Value = RowContent
RowNum = RowNum + 1
If i = UBound(Words) Then
RowContent = Words(i)
Else
RowContent = ""
End If
Else
RowContent = RowContent & Words(i) & " "
End If
Next i
If RowContent <> "" Then ActiveSheet.Range("A" & RowNum).Value = RowContent
End Sub

Related

why method "equals" skips the necessary chars although they are written in unicode?

I splitted csv-file by "," but some numbers were written with using of this symbol so some numbers were splitted on some parts. The beginning and the end of the every splitted number are signed by """ so i tried to paste parts of the number focusing on quotation marks...
string[] inputData = File.ReadAllLines("file.csv");
string[,] allData = new string[inputData.GetLength(0) - 1, 8];
for (int i = 1; i < inputData.GetLength(0); i++)
{
string[] arrayHelpStupied = inputData[i].Split(",");
string[] arrayHelpSmart = new string[8];
int m = 0;
int flagMistake = 0;
string compoundingOfSplittedNumber = "";
Console.WriteLine("\u0022");
for (int j = 0; j < arrayHelpStupied.Length; j++)
{
string prov = arrayHelpStupied[j];
if ((prov[0].Equals("\"")) || (flagMistake == 1))
{
flagMistake = 1;
compoundingOfSplittedNumber += arrayHelpStupied[j];
if (arrayHelpStupied[j][^1].Equals("\u0022"))
{
flagMistake = 0;
arrayHelpSmart[m++] = compoundingOfSplittedNumber;
continue;
}
continue;
}
arrayHelpSmart[m++] = arrayHelpStupied[j];
}
but numbers that start with quotation mark is ignored :(
please could you explain me the reason of such behaviour and how can i cope this difficulty please
If I'm reading the question correctly, you have a CSV file you're splitting on , and some of the "values" you're looking for also have a , in them and you don't want it to split on the , in the value...
Sorry, but it's not going to work. String.Split does not have any overrides, or regex matching. Your best bet is to sanitize your data first by removing the , from the values, or ensuring the , separating your values matches differently than the , in the values.
For example...
12,345 , 23,1566 , 111 , 1
you can split the above on " , " instead of just "," and since the pattern of " , " is consistent, then it will work
Alternately, you could open your csv in Excel and save it as a Tab delimited text file and then split on the tab "/t" character

Unicode have different width when printing

I am developing a chess engine in C#/Unity and want to print the board on a nice format. Preferably I want to print with some Unicode pieces but they end up making the board uneven, see image below:
The same seems to go for normal numbers since each row starts slightly off one another, row 1 starts more left than the others for example.
Why does my Debug.Log/prints end up like this, and how can I print them so that each character takes up the same amount of space?
EDIT:
Here is the code I use to Debug.Log the board if that helps:
public static void PrintBitboard(ulong bitboard)
{
string zero = " 0 ";
string one = " 1 ";
string printString = "";
// Loop through all squares and print a 1 if piece and 0 if not a piece on the square
for (int row = 0; row < 8; row++)
{
// Add numbering on the left side
printString += (8 - row) + " ";
for (int col = 0; col < 8; col++)
{
int currentSquare = row * 8 + col;
printString += BitOperations.GetBit(bitboard, currentSquare) != 0 ? one : zero;
}
// Change to new row
printString += "\n";
}
// Print bottom letters
printString += "\n" + " a b c d e f g h";
// Send message to the console
Debug.Log(printString);
}
What you are looking for is not "unicode" but monospace
-> As GiacomoCatenazzi already said, the only thing responsible for that is the font you are using
As a "quick and dirty" fix / alternative you could try and simply use tabs (\t) instead of spaces like (in general for larger string based concatenations I would recommend to use a StringBuider)
public static void PrintBitboard(ulong bitboard)
{
const string zero = "0\t";
const string one = "1\t";
var stringBuilder = new StringBuilder();
// Loop through all squares and print a 1 if piece and 0 if not a piece on the square
for (int row = 0; row < 8; row++)
{
// Add numbering on the left side
stringBuilder.Append((8 - row)).Append('\t');
for (int col = 0; col < 8; col++)
{
int currentSquare = row * 8 + col;
stringBuilder.Append(BitOperations.GetBit(bitboard, currentSquare) != 0 ? one : zero);
}
// Change to new row
stringBuilder.Append('\n');
}
// Print bottom letters
stringBuilder.Append("\n \ta\tb\tc\td\te\tf\tg\th");
// Send message to the console
Debug.Log(stringBuilder.ToString());
}
See .Net Fiddle which in the Unity console would look like
(in both I had to tricks a bit since I don't know what BitOperations implementation you are using)

Replace a character in specific position if line starts with condition C#

I am trying to modify a txt file, I need to change the 45 character with a P if the line starts with 8
for (int i = 0; i < textBox.Lines.Length; i++)//Loops through each line of text in RichTextBox
{
string text = textBox.Lines[i];
if ((text.Contains("8") == true)) //Checks if the line contains 8.
{
char replace = 'P';
int startindex = textBox.GetFirstCharIndexFromLine(i);
int endindex = text.Length;
textBox.Select(startindex, endindex);//Selects the text.
richTextBox1.Text = textBox.Text.Substring(0, textBox.SelectionStart) + (0, textBox.Lines) + replace + textBox.Text.Substring(textBox.SelectionStart + 45);
}}
To accomplish your goal the code could be changed in this way
//Loops through each line of text in RichTextBox
for (int i = 0; i < textBox.Lines.Length; i++)
{
string text = textBox.Lines[i];
//Checks if the line starts with "8".
if (text.StartsWith("8"))
{
// Find the 45th position from the start of the line
int startindex = textBox.GetFirstCharIndexFromLine(i) + 45;
// Starting from the found index select 1 char
textBox.Select(startindex, 1);
// Replace the selected char with the "P"
textBox.SelectedText = "P";
}
}
The key points changed are the way to select into a textbox. The Select method requires a starting index and the number of character to select, finally, once you have a SelectedText, (a read/write property) you can simply replace the current SelectedText with your own text. Lot easier than your current (and wrong) calculation.

Editing a line in a text file without using pointers?

I am trying to edit a line of a text file (.Hex file) containing all Hex characters without using pointers and in a more efficient way.
It takes so long because the program I have to edit some (around 30x4 bytes or 30 float values from the address values of hex file).
Every time the program replaces one byte, it searches the complete file and replaces the values, and copy back back again the new file to another file. This process repeats 30 times, which is quite time consuming and hence not looks appropriate.
What would be the most efficient method?
public static string putbyteinhexfile(int address, char data, string total)
{
int temph, temphl, tempht;
ushort checksum = 0;
string output = null, hexa = null;
StreamReader hex;
RegistryKey reg = Registry.CurrentUser;
reg = reg.OpenSubKey("Software\\Calibratortest");
hex = new StreamReader(((string)reg.GetValue("Select Input Hex File")));
StreamReader map = new StreamReader((string)reg.GetValue("Select Linker Map File"));
while ((output = hex.ReadLine()) != null)
{
checksum = 0;
temph = Convert.ToInt16(("0x" + output.Substring(3, 4)), 16);
temphl = Convert.ToInt16(("0x" + output.Substring(1, 2)), 16);
tempht = Convert.ToInt16(("0x" + output.Substring(7, 2)), 16);
if (address >= temph &&
address < temph + temphl &&
tempht == 0)
{
output = output.Remove((address - temph) * 2 + 9, 2);
output = output.Insert((address - temph) * 2 + 9,
String.Format("{0:X2}", Convert.ToInt16(data)));
for (int i = 1; i < (output.Length - 1) / 2; i++)
checksum += (ushort)Convert.ToUInt16(output.Substring((i * 2) - 1, 2), 16);
hexa = ((~checksum + 1).ToString("x8")).ToUpper();
output = output.Remove(temphl * 2 + 9, 2);
output = output.Insert(temphl * 2 + 9,
hexa.Substring(hexa.Length - 2, 2));
break;
}
else total = total + output + '\r' + '\n';
}
hex.Close();
map.Close();
return total;
}
Assuming you don't want to massively rewrite your existing logic which does 'for each line, do this search and replace logic', I'd think the simplest change would be:
var lines = File.ReadAllLines(filePath);
foreach (change to make)
{
for (int i = 0; i < lines.Length; i++)
{
// read values from line
if (need_to_modify)
{
// whatever change logic you want here.
lines[i] = lines[i].Replace(...);
}
}
}
File.WriteAllLines(filePath, lines);
Basically, you'll still do the logic you have now, except:
You read the file once instead of N times
you get rid of streamreader / streamwriter work
you do your changes on the array of strings in memory
string fileName = "blabla.hex";
StreamReader f1 = File.OpenText(fileName);
StreamWriter f2 = File.CreateText(fileName + ".temp_");
while (!f1.EndOfStream)
{
String s = f1.ReadLine();
//change the content of the variable 's' as you wish
f2.WriteLine(s);
}
f1.Close();
f2.Close();
File.Replace(fileName + ".temp_", fileName, null);

What is the algorithm to convert an Excel Column Letter into its Number?

I need an algorithm to convert an Excel Column letter to its proper number.
The language this will be written in is C#, but any would do or even pseudo code.
Please note I am going to put this in C# and I don't want to use the office dll.
For 'A' the expected result will be 1
For 'AH' = 34
For 'XFD' = 16384
public static int ExcelColumnNameToNumber(string columnName)
{
if (string.IsNullOrEmpty(columnName)) throw new ArgumentNullException("columnName");
columnName = columnName.ToUpperInvariant();
int sum = 0;
for (int i = 0; i < columnName.Length; i++)
{
sum *= 26;
sum += (columnName[i] - 'A' + 1);
}
return sum;
}
int result = colName.Select((c, i) =>
((c - 'A' + 1) * ((int)Math.Pow(26, colName.Length - i - 1)))).Sum();
int col = colName.ToCharArray().Select(c => c - 'A' + 1).
Reverse().Select((v, i) => v * (int)Math.Pow(26, i)).Sum();
Loop through the characters from last to first. Multiply the value of each letter (A=1, Z=26) times 26**N, add to a running total. My string manipulation skill in C# is nonexistent, so here is some very mixed pseudo-code:
sum=0;
len=length(letters);
for(i=0;i<len;i++)
sum += ((letters[len-i-1])-'A'+1) * pow(26,i);
Here is a solution I wrote up in JavaScript if anyone is interested.
var letters = "abc".toUpperCase();
var sum = 0;
for(var i = 0; i < letters.length;i++)
{
sum *= 26;
sum += (letters.charCodeAt(i) - ("A".charCodeAt(0)-1));
}
alert(sum);
Could you perhaps treat it like a base 26 number, and then substitute letters for a base 26 number?
So in effect, your right most digit will always be a raw number between 1 and 26, and the remainder of the "number" (the left part) is the number of 26's collected? So A would represent one lot of 26, B would be 2, etc.
As an example:
B = 2 = Column 2
AB = 26 * 1(A) + 2 = Column 28
BB = 26 * 2(B) + 2 = Column 54
DA = 26 * 4(D) + 1 = Column 105
etc
Shorter version:
int col = "Ab".Aggregate(0, (a, c) => a * 26 + c & 31); // 28
To ignore non A-Za-z characters:
int col = " !$Af$3 ".Aggregate(0, (a, c) => (uint)((c | 32) - 'a') > 25 ? a : a * 26 + (c & 31)); // 32
Here is a basic c++ answer for those who are intrested in c++ implemention.
int titleToNumber(string given) {
int power=0;
int res=0;
for(int i=given.length()-1;i>=0;i--)
{
char c=given[i];
res+=pow(26,power)*(c-'A'+1);
power++;
}
return res;
}
in Excel VBA you could use the .Range Method to get the number, like so:
Dim rng as Range
Dim vSearchCol as variant 'your input column
Set rng.Thisworkbook.worksheets("mySheet").Range(vSearchCol & "1:" & vSearchCol & "1")
Then use .column property:
debug.print rng.column
if you need full code see below:
Function ColumnbyName(vInput As Variant, Optional bByName As Boolean = True) As Variant
Dim Rng As Range
If bByName Then
If Not VBA.IsNumeric(vInput) Then
Set Rng = ThisWorkbook.Worksheets("mytab").Range(vInput & "1:" & vInput & "1")
ColumnbyName = Rng.Column
Else
MsgBox "Please enter valid non Numeric column or change paramter bByName to False!"
End If
Else
If VBA.IsNumeric(vInput) Then
ColumnbyName = VBA.Chr(64 + CInt(vInput))
Else
MsgBox "Please enter valid Numeric column or change paramter bByName to True!"
End If
End If
End Function
I guess this essentially works out pretty much the same as some of the other answers, but it may make a little more clear what's going on with the alpha equivalent of a numeric digit. It's not quite a base 26 system because there is no 0 placeholder. That is, the 26th column would be 'A0' or something instead of Z in base 26. And it's not base 27 because the 'alpha-gits' don't represent powers of 27. Man, it really makes you appreciate what a mess arithmetic must have been before the Babylonians invented the zero!
UInt32 sum = 0, gitVal = 1;
foreach (char alphagit in ColumnName.ToUpperInvariant().ToCharArray().Reverse())
{
sum += gitVal * (UInt32)(alphagit - 'A' + 1)
gitVal *= 26;
}
Like some others, I reversed the character array so I don't need to know anything about exponents.
For this purpose I use only one line:
int ColumnNumber = Application.Range[MyColumnName + "1"].Column;

Categories