Good evening formites
The task is: "Text file L4_16T.txt contains text. Delimiters are known. Move each longest word beginning with an uppercase letter to the beginning of the line with delimiters behind. Write the modified text to a separate Results.txt file. Prepare an Analysis file. txt in table format (2 columns): line number, word starting with a capital (upper) letter."
It is not possible to properly implement word wrapping in a line if there are two or more identical longest words in the line that begin with an uppercase letter.
That's because I'm looking for the maximum words in a string using IndexOf, which returns the first occurrence of the element. And since I have the same word several times in the line, the program each time transfers the same word to the beginning. And I need to transfer all the words.
I'll give you an example. There is this line:
" ,,,,,,,,,,,,,,,,,,,,,,,,Sdfsdf;;;;;;;;;;;;;;;sdfh Sdfsdf,,,,,,, ,,,,,,,,,,,,. Sdfsdf aaaaaaaaaaaaaaaaaaaaaaaaa
I should get the following output:
"Mdfsdf,,,,,,,,,Sdfsdf Sdfsdf,,,,,,,,,,,,,,,,,,,,.Sdfsdf;;;;;;;;;;;;;;; ,,,,,,,,,,,,,,,,,,,,,,,,sdfh aaaaaaaaaaaaaaaaaaaaaaaaa"
But for now I only get this line:
"Mdfsdf,,,,,,,,Sdfsdf;;;;;;;;;;;;;;;sdfh kj ,,,,,,,,,,,,,,,Sdfsdf ,,,,, ,,,,,,,,,,,,,,,.sdfsdf aaaaaaaaaaaaaaaaaaaaaaaaa
Tell me how can I solve this?
Program code:
using System;
using System.IO;
using System.Text;
namespace LAB_4_2
{
internal class Program
{
const string strFile = "TextFile.txt";
const string resFile = "Result.txt";
const string aFile = "Analiz.txt";
static void Main(string[] args)
{
char[] delimiters = { ' ', '.', ',', ';', ':', '[', ']', '(', ')', '!', '?', '\t', '\n' };
Process(strFile, aFile, resFile, delimiters);
}
static void Process(string strFile, string aFile, string resFile, char[] delimiters)
{
using (StreamReader readStr = new StreamReader(strFile, Encoding.GetEncoding(1257)))
{
using (StreamWriter writeA = new StreamWriter(aFile))
{
using (StreamWriter writeRes = new StreamWriter(resFile))
{
string top = "----------------------------\n" +
"| Line № | Longest word |\n" +
"----------------------------";
writeA.WriteLine(top);
string line;
int countline = 1;
while ((line = readStr.ReadLine()) != null) {
if (line.Length >= 0) {
string AllMaxWords = "";
string[] longestWords = FindMaxWords(line, delimiters, ref AllMaxWords);
if (AllMaxWords.Length == 0)
writeA.WriteLine("| {0, -4} | {1, -15} |", countline, "No words found");
else
for (int i = 0; i < longestWords.Length; i++)
{
if (longestWords[i] == "")
continue;
writeA.WriteLine("| {0, -4} | {1, -15} |", countline, longestWords[i]);
MoveToStart(ref line, longestWords[i], line.IndexOf(longestWords[i]));
}
countline++;
writeRes.WriteLine(line);
}
}
writeA.WriteLine("----------------------------");
}
}
}
}
static void MoveToStart(ref string myLine, string word, int start)
{
string tempLine = string.Empty;
int index = myLine.IndexOf(word) + word.Length;
while (index < myLine.Length)
{
if (myLine[index] == ' ')
{
tempLine += " ";
break;
}
else
{
tempLine += myLine[index];
index++;
}
}
string myLine1 = myLine.Remove(start, word.Length);
myLine1 = myLine1.Remove(myLine1.IndexOf(tempLine), tempLine.Length);
myLine = myLine1.Insert(0, word+tempLine);
}
static int MaxWordLength(string line, char[] delimiters)
{
int maxlength = 1;
string[] words = line.Split(delimiters,
StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < words.Length; i++)
{
if (Char.IsUpper(words[i][0]) &&
maxlength < words[i].Length)
{
maxlength = words[i].Length;
}
}
return maxlength;
}
static string[] FindMaxWords(string line, char[] delimiters, ref string AllMaxWords)
{
string[] temp = line.Split(delimiters,
StringSplitOptions.RemoveEmptyEntries);
int maxlength = MaxWordLength(line, delimiters);
for (int i = 0, k = 0; i < temp.Length; i++, k++)
{
if (Char.IsUpper(temp[i][0]) &&
maxlength == temp[i].Length)
{
AllMaxWords += temp[k] + " ";
}
}
return AllMaxWords.Split();
}
}
}
string keywords = "heard";
string strText = "Have you not heard!! what I said?"
string[] words = strText.Split(' ');
string result = "";
for (int i = 0; i < words.Length; i++)
{
if (words[i].Contains(keywords))
result += "<span>" + words[i] + "</span>" + " ";
else
result += words[i] + " ";
}
I get following output:
Have you not <span>heard!!</span> what I said?
Desired output:
Have you not <span>heard</span>!! what I said?
Can someone guide how can I get the desired output. The strText can only be split with a space.
Use String.Replace
var result = strText.Replace(keywords, "<span>" + keywords + "</span>");
If you have many keywords to replace, then just do replacement in a loop:
string[] keywords = { "heard", "said" };
string result = "Have you not heard!! what I said?";
foreach(var keyword in keywords)
result = result.Replace(keyword, "<span>" + keyword + "</span>");
Alternative solution is regular expression replacement:
string keywords = "heard|said";
string result = "Have you not heard!! what I said?";
result = Regex.Replace(result, keywords, m => "<span>" + m.Value + "</span>");
Why are you even looping through all words? This will give you the same:
string strText = "Have you not heard!! what I said?";
string newText = strText.Replace("heard", "<span>heard</span>");
I have a DataGridView with four Columns and need to crate a multiline string from its content, separated by comma.
This code works, but probably - there is a more elegant way:
string multiLine = "";
string singleLine;
foreach (DataGridViewRow r in dgvSm.Rows)
{
if (!r.IsNewRow)
{
singleLine = r.Cells[0].Value.ToString() + ","
+ r.Cells[1].Value.ToString() + ","
+ r.Cells[2].Value.ToString() + ","
+ r.Cells[3].Value.ToString() + Environment.NewLine;
multiLine = multiLine + singleLine;
}
}
I don't know about elegant, but:
use StringBuilder for string manipulation, type string is immutable!
if you need to do something in between, separate first or last cycle running (e.g. comma separation)
So, basically something like this:
StringBuilder multiLine = new StringBuilder();
foreach (DataGridViewRow r in dgvSm.Rows)
{
if (!r.IsNewRow)
{
if (r.Cells.Count > 0)
{
multiLine.Append(r.Cells[0].Value.ToString()); //first separated
for (int i = 1; i < r.Cells.Count; ++i)
{
singleLine.Append(','); //between values
singleLine.Append(r.Cells[i].Value.ToString());
}
multiLine.AppendLine();
}
}
}
To illustrate speed difference between StringBuilder concatenation (just dynamic array of characters) and string (new object and copy everything each time you use operator + concatenation), have a look at mini-program:
public static void Main()
{
var sw = new Stopwatch();
sw.Start();
StringBuilder s = new StringBuilder();
//string s = "";
int i;
for (i = 0; sw.ElapsedMilliseconds < 1000; ++i)
//s += i.ToString();
s.Append(i.ToString());
sw.Stop();
Console.WriteLine("using version with type " + s.GetType().Name + " I did " +
i + " times of string concatenation.");
}
For my computer it is:
using version with type String I did 17682 times of string concatenation.
using version with type StringBuilder I did 366367 times of string concatenation.
Try this :
string multiLine = "";
string singleLine;
foreach (DataGridViewRow r in dgvSm.Rows)
{
if (!r.IsNewRow)
{
singleLine = r.Cells[0].Value.ToString() + ","
+ r.Cells[1].Value.ToString() + ","
+ r.Cells[2].Value.ToString() + ","
+ r.Cells[3].Value.ToString() + "\r\n";
multiLine = multiLine + singleLine;
}
}
There has to be a better way to do this.
I just want to split long string into 60 character lines but do not break words. So it doesn't have to add up to 60 characters just has to be less than 60.
The code below is what I have and it works but I'm thinking there's a better way. Anybody?
Modified to use StringBuilder and fixed the problem of removing a repeating word.
Also don't want to use regex because I think that would be less efficient than what I have now.
public static List<String> FormatMe(String Message)
{
Int32 MAX_WIDTH = 60;
List<String> Line = new List<String>();
String[] Words;
Message = Message.Trim();
Words = Message.Split(" ".ToCharArray());
StringBuilder s = new StringBuilder();
foreach (String Word in Words)
{
s.Append(Word + " ");
if (s.Length > MAX_WIDTH)
{
s.Replace(Word, "", 0, s.Length - Word.Length);
Line.Add(s.ToString().Trim());
s = new StringBuilder(Word + " ");
}
}
if (s.Length > 0)
Line.Add(s.ToString().Trim());
return Line;
}
Thanks
Another (now TESTED) sample, very similiar to Keith approach:
static void Main(string[] args)
{
const Int32 MAX_WIDTH = 60;
int offset = 0;
string text = Regex.Replace(File.ReadAllText("oneline.txt"), #"\s{2,}", " ");
List<string> lines = new List<string>();
while (offset < text.Length)
{
int index = text.LastIndexOf(" ",
Math.Min(text.Length, offset + MAX_WIDTH));
string line = text.Substring(offset,
(index - offset <= 0 ? text.Length : index) - offset );
offset += line.Length + 1;
lines.Add(line);
}
}
I ran that on this file with all line breaks manually replaced by " ".
Try this:
const Int32 MAX_WIDTH = 60;
string text = "...";
List<string> lines = new List<string>();
StringBuilder line = new StringBuilder();
foreach(Match word in Regex.Matches(text, #"\S+", RegexOptions.ECMAScript))
{
if (word.Value.Length + line.Length + 1 > MAX_WIDTH)
{
lines.Add(line.ToString());
line.Length = 0;
}
line.Append(String.Format("{0} ", word.Value));
}
if (line.Length > 0)
line.Append(word.Value);
Please, also check this out: How do I use a regular expression to add linefeeds?
Inside a Regular expression, the Match Evaluator function (an anonymous method) does the grunt work and stores the newly sized lines into a StringBuilder. We don't use the return value of Regex.Replace method because we're just using its Match Evaluator function as a feature to accomplish line breaking from inside the regular expression call - just for the heck of it, because I think it's cool.
using System;
using System.Text;
using System.Text.RegularExpressions;
strInput is what you want to convert the lines of.
int MAX_LEN = 60;
StringBuilder sb = new StringBuilder();
int bmark = 0; //bookmark position
Regex.Replace(strInput, #".*?\b\w+\b.*?",
delegate(Match m) {
if (m.Index - bmark + m.Length + m.NextMatch().Length > MAX_LEN
|| m.Index == bmark && m.Length >= MAX_LEN) {
sb.Append(strInput.Substring(bmark, m.Index - bmark + m.Length).Trim() + Environment.NewLine);
bmark = m.Index + m.Length;
} return null;
}, RegexOptions.Singleline);
if (bmark != strInput.Length) // last portion
sb.Append(strInput.Substring(bmark));
string strModified = sb.ToString(); // get the real string from builder
It's also worth noting the second condition in the if expression in the Match Evaluator m.Index == bmark && m.Length >= MAX_LEN is meant as an exceptional condition in case there is a word longer than 60 chars (or longer than the set max length) - it will not be broken down here but just stored on one line by itself - I guess you might want to create a second formula for that condition in the real world to hyphenate it or something.
An other one ...
public static string SplitLongWords(string text, int maxWordLength)
{
var reg = new Regex(#"\S{" + (maxWordLength + 1) + ",}");
bool replaced;
do
{
replaced = false;
text = reg.Replace(text, (m) =>
{
replaced = true;
return m.Value.Insert(maxWordLength, " ");
});
} while (replaced);
return text;
}
I would start with saving the length of the original string. Then, start backwards, and just subtract, since odds are that I would get below 60 faster by starting at the last word and going back than building up.
Once I know how long, then just use StringBuilder and build up the string for the new string.
List<string> lines = new List<string>();
while (message.Length > 60) {
int idx = message.LastIndexOf(' ', 60);
lines.Add(message.Substring(0, idx));
message = message.Substring(idx + 1, message.Length - (idx + 1));
}
lines.Add(message);
You might need to modify a bit to handle multiple spaces, words with >60 chars in them, ...
I tried the original solution and found that it didn't quite work. I've modified it slightly to make it work. It now works for me and solves a problem I had. Thanks.
Jim.
public static List<String> FormatMe(String message)
{
int maxLength = 10;
List<String> Line = new List<String>();
String[] words;
message = message.Trim();
words = message.Split(" ".ToCharArray());
StringBuilder sentence = new StringBuilder();
foreach (String word in words)
{
if((sentence.Length + word.Length) <= maxLength)
{
sentence.Append(word + " ");
}
else
{
Line.Add(sentence.ToString().Trim());
sentence = new StringBuilder(word + " ");
}
}
if (sentence.Length > 0)
Line.Add(sentence.ToString().Trim());
return Line;
}
private void btnSplitText_Click(object sender, EventArgs e)
{
List<String> Line = new List<string>();
string message = "The quick brown fox jumps over the lazy dog.";
Line = FormatMe(message);
}