Multiline TextBox with custom word wrapping - c#

I am beginning to program in .Net and C# and currently I am stuck. I have a very similar problem as the posting on this question at stackoverflow : C#: Multiline TextBox with TextBox.WordWrap Displaying Long Base64 String.
The response to that question was this block of code:
public IEnumerable<string> SimpleWrap(string line, int length)
{
var s = line;
while (s.Length > length)
{
var result = s.Substring(0, length);
s = s.Substring(length);
yield return result;
}
yield return s;
}
I dont know how to make use of that piece of code. CAn someone please provide me with a code snippet that uses this particular method to write text that automatically also inserts a new line.
My code currently looks like this:
var length = GetMaximumCharacters(txtBxResults);
var txtWrap = SimpleWrap(stringValue, length);
foreach (string s in txtWrap)
{
txtBxResults.AppendText(s);
}
If I use AppendText method, it simple writes all the text in one single line which I do not want.
Any replies will be greatly appreciated.
Thanks,
KK

You almost have it right, you just need to insert the newline character as well. Try
foreach (string s in txtWrap)
{
txtBxResults.AppendText(s + Environment.NewLine);
}

Well I can't give you the exact code right now (I'll come back and post it later) but in general, what you should do is identify the index of the next comma and, if characters on the current line + that index > length of the line then append a new line before that compound. If you do that in a bucle, when it's done it should be formatted correctly, also take into account the last compound won't have (I think) a comma at the end.

Related

Get TextBox line number

I want to get a TextBox lines number in C# winform application. But unlike this question I want the real line number, whether it is a wrapped line or is a new line with \r\n.
var lines = tb.Lines.Count(); will just get the lines number with carriage return.
So How can I get what I'm looking for?
I dont understand what you want, but generally you can use this code to count lines in your textbox:
public int LineNumber()
{
int LineNumber;
LineNumber = textBoxNotePad.Lines.Length;
return LineNumber;
}
I hope it's useful

Searching for a phrase in text file then displaying that line

im trying to make a code that searches through a textfile for a certain phrase and then populates a textbox with the line if a phrase occurs in that. There are no errors with this code, but it doesn't work at all. Anyone know what is wrong? I'm not too sure if what i'm doing is remotely correct.
{
tuitDisplayTextBox.Text = "";
string[] tuitFilePath = File.ReadAllLines(Server.MapPath("~") +"/App_Data/tuitterMessages.txt");
for (int i = 0; i < tuitFilePath.Length; i++)
{
if (tuitFilePath[i].Contains(searchTextBox.Text))
{
tuitDisplayTextBox.Text += tuitFilePath[i];
}
}
Your solution should work... for the last line that matches, and only that one.
LINQ can help you here, though. Here's a solution that should work.
tuitDisplayTextBox.Text =
File.ReadLines(Server.MapPath("~") +"/App_Data/tuitterMessages.txt")
.Where(n => n.Contains(searchTextBox.Text)).Aggregate((a, b) =>
a + Enviroment.NewLine + b);
Here, what it does is it reads the lines of the file into an IEnumerable<string>, and then I filter that with the Where method, which basically means "if the condition is true for this element, add this element to the list of things to return, else don't add it". And then Aggregate is a bit more complicated. Basically what it does is it takes the first two items from the collection, and then pass a lambda through them that returns a value. Then call the lambda again with that result and the third element. And then it takes that result and calls it with the fourth element. And so on.
Here's some code more similar to yours that will also work:
tuitDisplayTextBox.Text = "";
IEnumerable<string> lines =
File.ReadAllLines(Server.MapPath("~") +"/App_Data/tuitterMessages.txt");
StringBuilder sb = new StringBuilder
foreach (string line in lines)
{
if (line.Contains(searchTextBox.Text))
{
sb.AppendLine(line);
}
}
tuitDisplayTextBox.Text = sb.ToString();
Here it's a bit different. First it reads all the lines into an IEnumerable<string> called lines. Then it makes a StringBuilder object (basically a mutable string). After that, it foreaches the lines in the IEnumerable<string> (I thought it was more appropriate here) and then if the line contains the text you want, it adds that line and a newline to the StringBuilder object. After that, it sets your textbox's text to the result of all of that, by getting the string representation of the StringBuilder instance.
And if you really want a for loop, here's the code modified to use a for loop:
tuitDisplayTextBox.Text = "";
string[] lines =
File.ReadAllLines(Server.MapPath("~") +"/App_Data/tuitterMessages.txt");
StringBuilder sb = new StringBuilder
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains(searchTextBox.Text))
{
sb.AppendLine(lines[i]);
}
}
tuitDisplayTextBox.Text = sb.ToString();
Please note that File.ReadAllLines break sentences at '\r' or '\n'.
So, if you search for "hello world" and this text is break in the file into 2 lines (e.g. "... hello /n world" your code will failed...
So, use the ReadAllText() instead, return one string contains all file's text.
Still, you might face sometimes problems with file encoding, but this is another issue.
After, and if, you find the text you are searching for you can use the ReadAllLines to decide about the location of the text.

Weird string behaviour, replaces parts of itself

I can't seem to wrap my head around this. How can this become:
string.Format(#"https://www.dropbox.com/s/{0}/{1}?dl=1", dl[rndIndex], rndIndex);
this:
/3?dl=1/www.dropbox.com/s/s8ghw2mvld2jg0l
It's like taking the part after {0} ,shifts it to the front and overrides the existing string...
Does anyone know what's going on here?
This is the entire code (pseudo):
string[] dl = new string[] { "...", "...", "..." };
int rndIndex = rnd.Next(0, dl.Length);
Console.WriteLine(string.Format(#"https://www.dropbox.com/s/{0}/{1}?dl=1", dl[rndIndex], rndIndex));
There's nothing wrong with dl[] and rndIndex, checked both of them.
This fixed the problem:
string s = dl[rndIndex];
s = s.Replace(((char)13).ToString(), "");
Which is what you suggested.
The way the it replaces the beginning of the URL, it seems that dl[rndIndex] contains a carriage return which places the cursor back to the beginning of the line and then overwrites the https:/ part of the URL (which fits as /3?dl=1 has the same length).
So your formatted string actually looks like this:
"https://www.dropbox.com/s/s8ghw2mvld2jg0l\r/3?dl=1"
^^
carriage return
Now when that is printed to a console which supports carriage returns, it will print the first part https://www.dropbox.com/s/s8ghw2mvld2jg0l then set the cursor back to the beginning and print the rest /3?dl=1.
So you should basically strip out all carriage returns from the string first. In any way it seems as if your dl array does not contain what you expect it to do.
I can reproduce the exact problem by #poke's comment:
string.Format("https://www.dropbox.com/s/{0}/{1}?dl=1", "hfjdhfjdh\r", 30)
will output:
/30?dl=1www.dropbox.com/s/hfjdhfjdh
The problem is a carriage return or new line character in your array elements.
Modifying your fragment as follows:
string[] dl = new string[] { "...", "...", "..." };
int rndIndex = 1; // rnd.Next(0, dl.Length);
Console.WriteLine(string.Format(#"https://www.dropbox.com/s/{0}/{1}?dl=1", dl[rndIndex], rndIndex));
Gives a correct answer.
https://www.dropbox.com/s/.../1?dl=1
So, two thoughts:
It's a data dependent problem
Should the random function not read rnd.Next(0, dl.Length-1)

Adding Control based on info in txt file

Following code, credit: Guffa.
Hello All,
I'm trying to add controls to a Form at runtime based on the information found in a Plain Text File. The structure? of the text file is always the same, and will not change. Example:
File.txt:
Label
"This is a label"
320, 240
Explanation:
Control
Text
Location
The following code, provided to me by Guffa, doesn't cause any errors or anything, but at the same time, nothing happens at all. And I'm not sure why... Can somebody please explain why the label doesn't get created and added to the form with the right info attached to it?
MatchCollection lines = Regex.Matches(File.ReadAllText(fileName), #"(.+?)\r\n""([^""]+)""\r\n(\d+), (\d+)\r\n");
foreach (Match match in lines) {
string control = match.Groups[1].Value;
string text = match.Groups[2].Value;
int x = Int32.Parse(match.Groups[3].Value);
int y = Int32.Parse(match.Groups[4].Value);
Console.WriteLine("{0}, \"{1}\", {2}, {3}", control, text, x, y);
if(control == "Label")
{
Label label = new Label();
label.Text = text;
canvas.Controls.Add(label); // canvas is a Panel Control.
label.Location = new Point(x, y);
}
}
I hope that I have clearly explained my situation. Any help at all would be greatly appreciated.
Thanks for taking the time to read.
jase
My guess is that your file doesn't have quite the right format. If you step into the code, does it match anything?
If so, what gets printed to the console?
Have you tried it with the exact sample shown in the question? While I haven't tried it in a form, I've tried the rest of the code above with the sample file, and it works fine.
Personally I don't think I'd use a regex to match all of the lines like this - it makes it harder to diagnose issues - but it should work okay if the file is correct. You say you don't understand the regex provided - that's another good reason not to use it, to be honest. Even if it's entirely correct, it's not a good idea to use code that you don't understand - you won't be able to maintain it.
I would personally just read the lines three at a time and then deal with them that way. Something like this:
private static readonly Regex LocationPattern = new Regex(#"^(\d+), (\d+)$");
...
using (TextReader reader = File.OpenText(filename))
{
while (true)
{
string control = reader.ReadLine();
string text = reader.ReadLine();
string location = reader.ReadLine();
if (control == null)
{
break;
}
if (text == null || location == null)
{
// Or however you want to handle this...
throw new InvalidConfigurationFileException
("Incorrect number of lines");
}
if (text.Length < 2 || !text.StartsWith("\"") || !text.EndsWith("\""))
{
// Or however you want to handle this...
throw new InvalidConfigurationFileException
("Text is not in quotes");
}
text = text.Substring(1, text.Length - 2);
Match locationMatch = LocationPattern.Match(location);
if (!locationMatch.Success)
{
// Or however you want to handle this...
throw new InvalidConfigurationFileException
("Invalid location: " + location);
}
// You could use int.TryParse if you want to handle this differently
Point parsedLocation = new Point(int.Parse(match.Groups[1].Value),
int.Parse(match.Groups[2].Value));
// Now the rest of the code before
}
}
As you can tell, it's a lot more code - but each part of it is relatively simple. Regular expressions are powerful if you're happy to handle them, but unless something is complicated to express "longhand" I often find it easier to maintain the longer way. Just a personal preference though.
Blind guess: I see the last \r\n in the regex is not optional. Possible that your input file is missing a return character after the last line?
Additional note about sucking at regular expressions: there are some tools that will help you experiment with regular expressions, which might be useful, for example, to understand what's going on in this particular case. I always use Expresso, which among other things analyzes the structure of the regular expression you provide and explains what it does.
Another possibility is that you forgot to add canvas (Panel control) to another control so the chain of controls inside canvas is not displayed and you would maybe not see that the canvas itself doesn't display. Which guess is the best? :-)

How do I handle line breaks in a CSV file using C#?

I have an Excel spreadsheet being converted into a CSV file in C#, but am having a problem dealing with line breaks. For instance:
"John","23","555-5555"
"Peter","24","555-5
555"
"Mary,"21","555-5555"
When I read the CSV file, if the record does not starts with a double quote (") then a line break is there by mistake and I have to remove it. I have some CSV reader classes from the internet but I am concerned that they will fail on the line breaks.
How should I handle these line breaks?
Thanks everybody very much for your help.
Here's is what I've done so far. My records have fixed format and all start with
JTW;...;....;...;
JTW;...;...;....
JTW;....;...;..
..;...;... (wrong record, line break inserted)
JTW;...;...
So I checked for the ; in the [3] position of each line. If true, I write; if false, I'll append on the last (removing the line-break)
I'm having problems now because I'm saving the file as a txt.
By the way, I am converting the Excel spreadsheet to csv by saving as csv in Excel. But I'm not sure if the client is doing that.
So the file as a TXT is perfect. I've checked the records and totals. But now I have to convert it back to csv, and I would really like to do it in the program. Does anybody know how?
Here is my code:
namespace EditorCSV
{
class Program
{
static void Main(string[] args)
{
ReadFromFile("c:\\source.csv");
}
static void ReadFromFile(string filename)
{
StreamReader SR;
StreamWriter SW;
SW = File.CreateText("c:\\target.csv");
string S;
char C='a';
int i=0;
SR=File.OpenText(filename);
S=SR.ReadLine();
SW.Write(S);
S = SR.ReadLine();
while(S!=null)
{
try { C = S[3]; }
catch (IndexOutOfRangeException exception){
bool t = false;
while (t == false)
{
t = true;
S = SR.ReadLine();
try { C = S[3]; }
catch (IndexOutOfRangeException ex) { S = SR.ReadLine(); t = false; }
}
}
if( C.Equals(';'))
{
SW.Write("\r\n" + S);
i = i + 1;
}
else
{
SW.Write(S);
}
S=SR.ReadLine();
}
SR.Close();
SW.Close();
Console.WriteLine("Records Processed: " + i.ToString() + " .");
Console.WriteLine("File Created SucacessFully");
Console.ReadKey();
}
}
}
CSV has predefined ways of handling that. This site provides an easy to read explanation of the standard way to handle all the caveats of CSV.
Nevertheless, there is really no reason to not use a solid, open source library for reading and writing CSV files to avoid making non-standard mistakes. LINQtoCSV is my favorite library for this. It supports reading and writing in a clean and simple way.
Alternatively, this SO question on CSV libraries will give you the list of the most popular choices.
Rather than check if the current line is missing the (") as the first character, check instead to see if the last character is a ("). If it is not, you know you have a line break, and you can read the next line and merge it together.
I am assuming your example data was accurate - fields were wrapped in quotes. If quotes might not delimit a text field (or new-lines are somehow found in non-text data), then all bets are off!
There is a built-in method for reading CSV files in .NET (requires Microsoft.VisualBasic assembly reference added):
public static IEnumerable<string[]> ReadSV(TextReader reader, params string[] separators)
{
var parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(reader);
parser.SetDelimiters(separators);
while (!parser.EndOfData)
yield return parser.ReadFields();
}
If you're dealing with really large files this CSV reader claims to be the fastest one you'll find: http://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader
I've used this piece of code recently to parse rows from a CSV file (this is a simplified version):
private void Parse(TextReader reader)
{
var row = new List<string>();
var isStringBlock = false;
var sb = new StringBuilder();
long charIndex = 0;
int currentLineCount = 0;
while (reader.Peek() != -1)
{
charIndex++;
char c = (char)reader.Read();
if (c == '"')
isStringBlock = !isStringBlock;
if (c == separator && !isStringBlock) //end of word
{
row.Add(sb.ToString().Trim()); //add word
sb.Length = 0;
}
else if (c == '\n' && !isStringBlock) //end of line
{
row.Add(sb.ToString().Trim()); //add last word in line
sb.Length = 0;
//DO SOMETHING WITH row HERE!
currentLineCount++;
row = new List<string>();
}
else
{
if (c != '"' && c != '\r') sb.Append(c == '\n' ? ' ' : c);
}
}
row.Add(sb.ToString().Trim()); //add last word
//DO SOMETHING WITH LAST row HERE!
}
Try CsvHelper (a library I maintain). It ignores empty rows. I believe there is a flag you can set in FastCsvReader to have it handle empty rows also.
Heed the advice from the experts and Don't roll your own CSV parser.
Your first thought is, "How do I handle new line breaks?"
Your next thought is, "I need to handle commas inside of quotes."
Your next thought will be, "Oh, crap, I need to handle quotes inside of quotes. Escaped quotes. Double quotes. Single quotes..."
It's a road to madness. Don't write your own. Find a library with an extensive unit test coverage that hits all the hard parts and has gone through hell for you. For .NET, use the free CsvHelper library.
Maybe you could count for (") during the ReadLine(). If they are odd, that will raise the flag. You could either ignore those lines, or get the next two and eliminate the first "\n" occurrence of the merge lines.
What I usually do is read the text in character by character opposed to line by line, due to this very problem.
As you're reading each character, you should be able to figure out where each cell starts and stops, but also the difference between a linebreak in a row and in a cell: If I remember correctly, for Excel generated files anyway, rows start with \r\n, and newlines in cells are only \r.
There is an example parser is c# that seems to handle your case correctly. Then you can read your data in and purge the line breaks out of it post-read.
Part 2 is the parser, and there is a Part 1 that covers the writer portion.
Read the line.
Split into columns(fields).
If you have enough columns expected for each line, then process.
If not, read the next line, and capture the remaining columns until you get what you need.
Repeat.
A somewhat simple regular expression could be used on each line. When it matches, you process each field from the match. When it doesn't find a match, you skip that line.
The regular expression could look something like this.
Match match = Regex.Match(line, #"^(?:,?(?<q>['"](?<field>.*?\k'q')|(?<field>[^,]*))+$");
if (match.Success)
{
foreach (var capture in match.Groups["field"].Captures)
{
string fieldValue = capture.Value;
// Use the value.
}
}
Have a look at FileHelpers Library
It supports reading\writing CSV with line breaks as well as reading\writing to excel
The LINQy solution:
string csvText = File.ReadAllText("C:\\Test.txt");
var query = csvText
.Replace(Environment.NewLine, string.Empty)
.Replace("\"\"", "\",\"").Split(',')
.Select((i, n) => new { i, n }).GroupBy(a => a.n / 3);
You might also check out my CSV parser SoftCircuits.CsvParser on NuGet. It will not only parse a CSV file but--if wanted--can also automatically map column values to your class properties. And it runs nearly four times faster than CsvHelper.
For a line break to exist in a CSV, there must be an open double quote that's not closed.
Assuming that all CSVs cells must open and close a double quote, just check if there's an odd number of quotation marks
my_string.Count(c => c == '"') % 2 == 1
and if that's the case, continue reading until you have the even number.

Categories