Message Parser C# - c#

I've been trying to make some kind of message parser, that gets only my sent messages. For example if i had message like that:
Viktor Bale (11 aug. 2016 13:20:56):
Hi! How are you?
Not Viktor Bale (11 aug. 2016 13:20:56):
Hi! Good! And you?
Viktor Bale (11 aug. 2016 13:20:56):
Me too! And this message has
Two lines!
Not Viktor Bale (11 aug. 2016 13:20:56):
And this doesn't matter!
I need to get only messages written by Viktor Bale
here is code, that i tryed:
for (int i = 0; i < wordsList.Count; i++)
{
if (wordsList[i].StartsWith(defaultName))
{
while (!wordsList[i].StartsWith(dialName))
{
messages.Add(wordsList[i]);
}
}
}
wordsList is list of my messages, recieved from txt file and read by ReadAllLines
So messages above is just list.
defaultName is my name, and dialName is name of my interlocutor.
But when i launch it, my app simply freezes. How should i do that?

You are forgetting to increment i:
for (int i = 0; i < wordsList.Count; i++)
{
if (wordsList[i].StartsWith(defaultName))
{
while (i < worldList.Count && !wordsList[i].StartsWith(dialName))
{
messages.Add(wordsList[i++]);
}
}
}
Edit: Added a safety bounds check.

The while loop will never come to an end.
Perhaps you meant something like this? I've tidied up your code and made it simpler.
foreach (var words in wordsList)
{
if (words.StartsWith(defaultName) && !words.StartsWith(dialName))
{
messages.Add(wordsList[i]);
}
}

you should be able to select your messages using linq assuming that each line begins with the name of the sender and the message does not include a line break.
e.g.
var myMessages = wordsList.Where(x => x.StartsWith(defaultName))
The application is crashing on your while loop which simply evaluates a condition infinity but never does anything to change it.

To avoid endless while loop, use this code instead :
for (int i = 0; i < wordsList.Count; i++)
{
if (wordsList[i].StartsWith(defaultName))
{
if (!wordsList[i].StartsWith(dialName))
{
messages.Add(wordsList[i]);
}
}
}
OR
you can use something simpler to achieve desired behaviour:
foreach (var word in wordsList)
{
if (word.StartsWith(defaultName))
{
messages.Add(word);
}
}
hope it helps

Here is an alternative to do that so:
public static string ExtractSenderName(string line) {
var i = line.IndexOf('(');
if (i == -1)
return string.Empty;
return line.Substring(0, i).Trim();
}
public static void Main (string[] args) {
var messages = new List<string>();
for (int i = 0; i < wordsList.Length; i++)
{
if (ExtractSenderName(wordsList[i]) == defaultName) {
messages.Add(wordsList[++i]);
}
}
foreach (var x in messages) {
Console.WriteLine(x);
}
}
Here is the demo

Related

Teaching myself C#. Don't know where/what I need to fix (Object reference not set to instance of an object. line 25) [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
What does "Object reference not set to an instance of an object" mean? [duplicate]
(8 answers)
Closed 2 years ago.
I keep getting the error in title. I am not very experienced in coding and am not great at reading code and understanding it yet.
I also know this will be a simple fix but still I don't know what or where I need to fix.
using System;
using System.Linq;
namespace Day_6
{
class Program
{
static void Main(string[] args)
{
int numStrings = Convert.ToInt32(Console.ReadLine());
var str = "";
string[] words = new string[1000];
var even = new string[500];
var odd = new string[500];
for (int i = 0; i < numStrings; i++)
{
str = Console.ReadLine();
words.Append(str);
}
foreach (var word in words)
{
foreach (var letter in word)
{
if (word.IndexOf(letter)%2 != 0)
{
odd.Append(letter.ToString());
}
else
{
even.Append(letter.ToString());
}
}
Console.WriteLine(odd + " " + even);
}
}
}
}
Any help, even if it is just material to read so I can understand why/what/where I am getting this error would be great.
After all I am trying to learn!
Many thanks
If you have to use Arrays like you are doing, change your assignment for loop to the following,
for (int i = 0; i < numStrings; i++)
{
str = Console.ReadLine();
words[i] = str; // Assign to elements of Array (not Append).
}
and then you want to iterate over words that are not null (initial array is all nulls). You will need to use index for each of the even and odd arrays as well. You cannot use indexOf either to check if your character is even or odd... if you do that, then any duplicate letter will not work. Use an iteration index for that as well.
int evenIndex = 0;
int oddIndex = 0;
int iterationIndex = 0;
foreach (var word in words.Where(x => x != null))
{
foreach (var letter in word)
{
if (iterationIndex++ % 2 != 0)
{
odd[oddIndex++] = (letter.ToString());
}
else
{
even[evenIndex++] = (letter.ToString());
}
}
}
// Print this "outside" of your for loops
Console.WriteLine(string.Concat(odd.Where(x => x != null)) + " " + string.Concat(even.Where(x => x != null)));
You will also need to change your Console.WriteLine statement at the end to print the elements of the arrays instead of printing the type of the string array.
Console.WriteLine(string.Concat(odd.Where(x => x != null)) + " --- " + string.Concat(even.Where(x => x != null)));
Test Inputs and output
2
abababababababababababababab
abababababababababababababab
bbbbbbbbbbbbbbbbbbbbbbbbbbbb aaaaaaaaaaaaaaaaaaaaaaaaaaaa
Alternate Suggestion that doesnt use indexers
foreach (string word in words.Where(x => x != null))
{
for (int i = 0; i < word.Count(); i++)
{
if (i % 2 == 0)
odd[i] = word[i].ToString();
else
even[i] = word[i].ToString();
}
}
The first instance of null-reference error is caused by line 17 where you have words.Append(str);, replace it with words[i] = str;.
Read about the behaviour of IEnumerable.Append() method here, because it does not modify the array words in place; it returns a new IEnumerable, which must then be enumerated to an array.
So the statement words.Append(str); actually does nothing; if you wish to use the Append() method, you must save it's return value and enumerate the result to an array like so:
words = words.Append(str).ToArray()
Also, there's a second null-reference error in the 3rd statement in your Main() method (line 9), where you have
string[] words = new string[1000];
If you replace it with:
string[] words = new string[numStrings];
You will remove the possibility of your foreach loop looping over null elements.

How can I prevent StackOverflowException in my code

In my code, I cannot figure out why I keep getting 'Process is terminating due to StackOverflowException.' only on the second output.
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
namespace _2018JuniorQ5
{
class Program
{
//Variable Decleration
public static int pages = 0;
public static string[] bookFormat;
public static List<string> alreadyChecked = new List<string>();
public static List<string> nodesToCheck = new List<string>();
public static int level = 0;
public static List<string> childrenNodes = new List<string>();
public static void Main(string[] args)
{
//Get input
pages = Convert.ToInt32(Console.ReadLine());
bookFormat = new string[pages];
for (int x=0; x<pages; x++)
{
bookFormat[x] = Console.ReadLine();
}
//Display if all pages are reachable
Console.WriteLine(getReachablePages(1));
//Find shortest path
List<string> NodeBegin = new List<string>();
NodeBegin.Add("1");
Console.WriteLine(getShortestPath(NodeBegin));
}
public static string getReachablePages(int pageToCheck)
{
string[] options=(bookFormat[pageToCheck - 1]).Split(' ');
alreadyChecked.Add(Convert.ToString(pageToCheck));
for (int a=1; a<=Convert.ToInt32(options[0]); a++)
{
if (!alreadyChecked.Contains(options[a]))
{
getReachablePages(Convert.ToInt32(options[a]));
}
}
if (alreadyChecked.Count == pages)
{
return "Y";
}
else
{
return "N";
}
alreadyChecked.Clear();
}
public static int getShortestPath(List<string> nodesToCheck)
{
level++;
childrenNodes.Clear();
for (int q = 0; q < nodesToCheck.Count; q++)
{
string[] options = bookFormat[Convert.ToInt32(nodesToCheck[q])-1].Split(' ');
if (options[0] == "0")
{
return level;
}
else
{
for (int t = 1; t < options.Length; t++)
{
if (!alreadyChecked.Contains(options[t]))
{
childrenNodes.Add(options[t]);
alreadyChecked.Add(nodesToCheck[q]);
}
}
}
nodesToCheck.Clear();
}
return getShortestPath(childrenNodes);
}
}
}
The first output from the getReachablePages method works, and does not give any errors. However, the second output from the getShortestPath gives the "Process is terminating due to StackOverflowException" error. Can someone explain why the getReachablePages method works, but the getShortestPath method doesn't work?
The problem at the moment is that List<string> is a reference type, so when you pass childrenNodes to getShortestPath (getShortestPath(childrenNodes)), you're actually passing a reference to the same list in memory.
The next thing you do is call childrenNodes.Clear(), which empties that list. Because nodesToCheck and childrenNodes are both pointing at the same list, calling childrenNodes.Clear() means that you have no nodes to check.
Why does this cause a StackOverflowException? It causes one because you have no exit condition for when nodesToCheck is empty. You just keep calling the same method over and over.
I propose the following solution:
public static int getShortestPath(List<string> nodesToCheck)
{
if (!nodesToCheck.Any())
{
return -1;
}
var childrenNodes = new List<string>();
level++;
for (int q = 0; q < nodesToCheck.Count; q++)
{
string[] options = bookFormat[Convert.ToInt32(nodesToCheck[q])-1].Split(' ');
if (options[0] == "0")
{
return level;
}
else
{
for (int t = 1; t < options.Length; t++)
{
if (!alreadyChecked.Contains(options[t]))
{
childrenNodes.Add(options[t]);
alreadyChecked.Add(nodesToCheck[q]);
}
}
}
nodesToCheck.Clear();
}
return getShortestPath(childrenNodes);
}
When nodesToCheck is empty, return -1 (i.e. no path).
Create a new List<string> for childrenNodes within the getShortestPath method.
While this should fix your problem, I would recommend making your entire method self-contained. It's essentially a stateless method, but you're maintaining state outside the method, which led to the problem you have seen, and could lead to more problems if you call this method in a multi-threaded environment.
The other odd thing I noticed, is that you're looping through nodesToCheck, but the way your code is written means that you will only ever consider the first node because you clear nodesToCheck at the end of the first iteration, leaving the list empty. Further more, you're adding nodesToCheck[q] to alreadChecked once per item in options, which I'm sure can't be right.
I recommend learning to use the debugger in Visual Studio. The debugger allows you to step through your code line-by-line, inspect variables, etc. - this will help you locate problems in your code.
P.S. While it is not the correct solution to your problem, if you wish to copy a list you can use LINQ's ToList() method: var listB = listA.ToList();

Foreach MUCH faster than For in some cases?

I don't like premature optimization but I was curious while doing a simple task
so I added a stopwatch.
I don't understand how the difference can be so big.
Array of strings (7 characters) each [richtextbox.text].
Length of array: 5500 elements.
Foreach Elapsed Time: 0.0015 seconds
For Elapsed Time: 9.757 seconds
For:
if (chkLineBreaks.Checked)
{
for (int i = 0; i < txtInput.Lines.Length; i++)
{
outputStringBuilder.Append($#"'{txtInput.Lines[i]}',");
}
}
Foreach:
foreach (var line in txtInput.Lines)
{
outputStringBuilder.Append($#"'{line}',");
if (chkLineBreaks.Checked)
outputStringBuilder.AppendLine();
}
From what I've read the difference should be negligible and the For would slightly faster.
Even more, the foreach has a condition in each iteration (unless it is being 'hoisted' up before the loop.
What is going on here?
Edit:
I've changed the foreach code to:
int i = 0;
foreach (var line in txtInput.Lines)
{
outputStringBuilder.Append($#"'{txtInput.Lines[i]}',");
i++;
}
So it is now doing the same thing.
It is taking 4.625 seconds.. still about half of the time for the FOR
Also I know that I can extract the array outside the loop but this is not what I am testing here :)
Edit #2:
This is the whole code for that section:
Stopwatch sw = new Stopwatch();
sw.Start();
// for (int i = 0; i < txtInput.Lines.Length; i++)
// {
// outputStringBuilder.Append($#"'{txtInput.Lines[i]}',");
// }
int i = 0;
foreach (var line in txtInput.Lines)
{
outputStringBuilder.Append($#"'{txtInput.Lines[i]}',");
i++;
}
MessageBox.Show(sw.Elapsed.ToString());
The issue is that txtInput.Lines is executing many times (once per line) in your for loop (due to use of txtInput.Lines[i]). So for every line of the file you are saying 'OK, please parse this textbox into multiple lines - and then get me the nth line' - and the parsing is the killer bit.
For a fairer comparison:
if (chkLineBreaks.Checked)
{
var lines = txtInput.Lines;
for (int i = 0; i < lines.Length; i++)
{
outputStringBuilder.Append($#"'{lines[i]}',");
}
}
this way the Lines call is done only once (i.e. equivalent to the foreach scenario).
One way to spot these kinds of issues is to compare the timings. The slow one is about 6K slower than the fast one, and you have 5.5K entries. Since 5.5K and 6K are very similar numbers, it may prompt you to think 'am I doing something in the loop that I really shouldn't?'
The compiled code sees very little difference between a for and a foreach statement when traversing an array (or list).
Consider this simple code the writes out a list of strings three different ways:
class Program
{
static void Main(string[] args)
{
var list = Enum.GetNames(typeof(System.UriComponents));
// 1. for each
foreach (var item in list)
{
Console.WriteLine(item);
}
// 2. for loop
for (int i = 0; i<list.Length; i++)
{
Console.WriteLine(list[i]);
}
// 3. LINQ
Console.WriteLine(string.Join(Environment.NewLine, list));
}
}
Now look at the MSIL compiled code, translated back into C# using ILSpy or DotNetPeek.
// ConsoleApplication1.Program
private static void Main(string[] args)
{
string[] list = Enum.GetNames(typeof(UriComponents));
string[] array = list;
for (int j = 0; j < array.Length; j++)
{
string item = array[j];
Console.WriteLine(item);
}
for (int i = 0; i < list.Length; i++)
{
Console.WriteLine(list[i]);
}
Console.WriteLine(string.Join(Environment.NewLine, list));
}
See the two for loops. The foreach statement became a for loop by the compiler. As far as the string.Join() statement it calls the SZArrayEnumerator which holds a reference to the array, and the current index value. At each .MoveNext() call the index is incremented and a new value returned. Basically, it is equivalent to the following:
int i = 0;
while (i<list.Length)
{
Console.WriteLine(list[i]);
i++;
}

Trying to find specific word inside string without contain method. code isn't working the way it should

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication18
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Sisesta suvaline tekst-->");
string tekst1 = Console.ReadLine();
// string tekst2 = ("ja");
char jtaht = ('j');
char ataht = ('a');
int jsidOntekstis = 0;
int asidOnTekstis = 0;
int tekstipikkus = tekst1.Length;
int jasidonTekstis = jsidOntekstis + asidOnTekstis;
int jasidEiOleTekstis=1;
for (int i = 0; i < tekstipikkus; i++)
{
if (tekst1[i] == jtaht)
{
jsidOntekstis++;
}
if (tekst1[i] == ataht)
{
asidOnTekstis++;
}
}
// for (int k = 0; i < tekstipikkus; i++)
{
{
if (jasidonTekstis > jasidEiOleTekstis)
{
Console.Write("Ja on tekstis olemas");
}
else
{
Console.Write("Ja-sid ei ole tekstis");
}
}
}
Console.ReadKey();
}
}
}
So This is my code and it isn't working the way it should. My teacher asked me to search for "ja" in text without contain method so we would think more logically. I completed all other exercises but this one. Thank you!
StackOverflow is actually not a place where people DO something for you.
They help you and tell you HOW to do this. This issue contains only the wrong piece of code and question "what's wrong".
First of all, I need to tell you that the first problem is, obviously, the algorithm.
I can't understand what is your code supposed to do because even you don't understand it.
using System;
using System.Text;
namespace ConsoleApplication18
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Where to search -->");
string text = Console.ReadLine();
string pattern = "ja"; // Probably, it is better to get it from Console as well
for (int i = 0; i < text.Length; i++)
{
for (int j = 0; j < pattern.Length; j++)
{
if (text[i+j] == pattern[j] && j == pattern.Length - 1)
Console.WriteLine(i);
if (text[i+j] != pattern[j]) break;
}
}
}
}
}
Here is the (not a best) code which searches for the pattern in the text without Contains method. It iterates through the text string - and if it meets the first character of pattern string - it goes further comparing characters one by one in a row. If the inner loop iterated till the end then it means that text string contains pattern string and outputs it's position. If in any moment characters are not equal then the inner loop breaks and continues the main loop.
Research it and understand it. Then you can solve the problem.

Why is my IEnumerable<String> using yield return slower to iterate then List<String>

I have been testing out the yield return statement with some of the code I have been writing. I have two methods:
public static IEnumerable<String> MyYieldCollection {
get
{
wrapper.RunCommand("Fetch First From Water_Mains");
for (int row = 0; row < tabinfo.GetNumberOfRows() ; row++) //GetNumberOfRows
//will return 1000+ most of the time.
{
yield return wrapper.Evaluate("Water_Mains.col1");
wrapper.RunCommand("Fetch Next From Water_Mains");
}
}
}
and
public static List<String> MyListCollection
{
get
{
List<String> innerlist = new List<String>();
wrapper.RunCommand("Fetch First From Water_Mains");
for (int row = 0; row < tabinfo.GetNumberOfRows(); row++)
{
innerlist.Add(wrapper.Evaluate("Water_Mains.col1"));
wrapper.RunCommand("Fetch Next From Water_Mains");
}
return innerlist;
}
}
then I use a foreach loop over each collection:
foreach (var item in MyYieldCollection) //Same thing for MyListCollection.
{
Console.WriteLine(item);
}
The funny thing is for some reason I seem to be able to loop over and print out the full MyListCollection faster then the MyYieldCollection.
Results:
MyYieldCollection -> 2062
MyListCollection -> 1847
I can't really see a reason for this, am I missing something or is this normal?
How have you done your timings? Are you in the debugger? In debug mode? It looks like you are using DataTable, so I used your code as the template for a test rig (creating 1000 rows each time), and used the harness as below, in release mode at the command line; the results were as follows (the number in brackets is a check to see they both did the same work):
Yield: 2000 (5000000)
List: 2100 (5000000)
Test harness:
static void Main()
{
GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
int count1 = 0;
var watch1 = Stopwatch.StartNew();
for(int i = 0 ; i < 5000 ; i++) {
foreach (var row in MyYieldCollection)
{
count1++;
}
}
watch1.Stop();
GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
int count2 = 0;
var watch2 = Stopwatch.StartNew();
for (int i = 0; i < 5000; i++)
{
foreach (var row in MyListCollection)
{
count2++;
}
}
watch1.Stop();
Console.WriteLine("Yield: {0} ({1})", watch1.ElapsedMilliseconds, count1);
Console.WriteLine("List: {0} ({1})", watch2.ElapsedMilliseconds, count2);
}
(note you shouldn't normally use GC.Collect, but it has uses for levelling the field for performance tests)
The only other change I made was to the for loop, to avoid repetition:
int rows = tabinfo.Rows.Count;
for (int row = 0; row < rows; row++) {...}
So I don't reproduce your numbers...
What happens if one iteration of your loop is expensive and you only need to iterate over a few items in your collection?
With yield you only need to pay for what you get ;)
public IEnumerable<int> YieldInts()
{
for (int i = 0; i < 1000; i++)
{
Thread.Sleep(1000) // or do some other work
yield return i;
}
}
public void Main()
{
foreach(int i in YieldInts())
{
Console.WriteLine(i);
if(i == 42)
{
break;
}
}
}
My guess is that the JIT can better optimize the for loop in the version that returns the list. In the version that returns IEnumerable, the row variable used in the for loop is now actually a member of a generated class instead of a variable that is local only to the method.
The speed difference is only around 10%, so unless this is performance critical code I wouldn't worry about it.
As far as I understand it, "yield return" will keep looping until it runs our of stuff to do and the function/property exits, returning a filled IEnumarable. In other words instead of the function being called for each item in the foreach loop, it is called once and before anything inside the foreach loop is executed.
It could be by the type of collections that are returned. Perhaps the List can be iterated over faster than whatever datastructure the IEnumerable is.

Categories

Resources