I am doing a message loop for a skype bot, it would means I can advertise/mass message easily. However, after the first name string in the loop get replaced by the name in the class, it will only use that name and not replace the new name on the next loop.
Code:
if(listView2.SelectedItems.Count<=0)
return;
string value="Hi %name%!";
string body="";
if(Program.InputBox("New Message", "Body of the message:", ref value)==DialogResult.OK) {
body=value;
}
foreach(ListViewItem i in listView2.SelectedItems) {
String rawID=i.SubItems[7].ToString();
String[] splitID=rawID.Split('}');
String[] ID=splitID[0].Split(new char[] { '{' });
body=body.Replace("%handle%", SkypeUser.Users[Convert.ToInt32(ID[1])].Handle);
body=body.Replace("%name%", SkypeUser.Users[Convert.ToInt32(ID[1])].Name);
body=body.Replace("%status%", SkypeUser.Users[Convert.ToInt32(ID[1])].Status);
body=body.Replace("%country%", SkypeUser.Users[Convert.ToInt32(ID[1])].Country);
body=body.Replace("%mood%", SkypeUser.Users[Convert.ToInt32(ID[1])].Mood);
body=body.Replace("%about%", SkypeUser.Users[Convert.ToInt32(ID[1])].About);
if(!(body==String.Empty)) {
skype.SendMessage(SkypeUser.Users[Convert.ToInt32(ID[1])].Handle, body);
}
}
If I select three people, named Jim, Tim and Derp, then the first person on the selected list is Jim. If I used %name%, it will be correctly replaced to "Jim" in the message to Jim, but the messages to Tim and Derp will also have "Jim", instead of the string replacing %name% to their names.
Edit:
I know putting value, body and if-statements inside the loop; but I want that requiring to input message would be only once. That's the whole point of the mass message.
following statement should be inside the for loop.
string value = "Hi %name%!";
string body = "";
if (Program.InputBox("New Message", "Body of the message:", ref value) == DialogResult.OK)
{
body = value;
}
First time you go through the loop, the %% placeholders are effectively destroyed. If you go in with:
body = "Hi %name%";
after the first iteration you get:
body = "Hi Jim";
When the loop runs for the second time it searches for %name% in "Hi Jim", does not find anything to replace, leaves the string alone, and you end up sending "Hi Jim" to Derp. Avoid modifying the original value of the body, and use a fresh variable in each iteration:
foreach (ListViewItem i in listView2.SelectedItems)
{
string userBody = body;
...
userBody = userBody.Replace("%name%", SkypeUser.Users[Convert.ToInt32(ID[1])].Name);
...
}
Also note that the string class in C# is immutable, which means that each string operation in the loop creates a new instance of a string with modified contents, and leaves the previous value as garbage to be collected. If you do significant string manipulation (and you do) look into the StringBuilder class, that is designed to used in string manipulation. Your code will look somewhat like:
foreach (ListViewItem i in listView2.SelectedItems)
{
StringBuilder userBodyBuilder = new StringBuilder(body);
...
userBodyBuilder.Replace("%name%", SkypeUser.Users[Convert.ToInt32(ID[1])].Name);
...
}
This code will waste significantly less memory and be significantly faster than your original code, so you'll be able to spam your contacts more efficiently :D
I think that the first time you replace your data %XXX% ("%name%" for example), the next time you pass in the loop you don't have them anymore.
Use a new variable in your loop :
foreach[...]
{
string currentBody = body;
currentBody = body.Replace[...]
[...]
}
Just replace the string again after sending the message<3
Easy code for you to copy
if(SkypeUser.Users[Convert.ToInt32(ID[1])].Handle!=String.Empty) {
body=body.Replace(SkypeUser.Users[Convert.ToInt32(ID[1])].Handle, "%handle%");
}
if(SkypeUser.Users[Convert.ToInt32(ID[1])].Name!=String.Empty) {
body=body.Replace(SkypeUser.Users[Convert.ToInt32(ID[1])].Name, "%name%");
}
if(SkypeUser.Users[Convert.ToInt32(ID[1])].Status!=String.Empty) {
body=body.Replace(SkypeUser.Users[Convert.ToInt32(ID[1])].Status, "%status%");
}
if(SkypeUser.Users[Convert.ToInt32(ID[1])].Country!=String.Empty) {
body=body.Replace(SkypeUser.Users[Convert.ToInt32(ID[1])].Country, "%country%");
}
if(SkypeUser.Users[Convert.ToInt32(ID[1])].Mood!=String.Empty) {
body=body.Replace(SkypeUser.Users[Convert.ToInt32(ID[1])].Mood, "%mood%");
}
if(SkypeUser.Users[Convert.ToInt32(ID[1])].About!=String.Empty) {
body=body.Replace(SkypeUser.Users[Convert.ToInt32(ID[1])].About, "%about%");
}
That's because you are replacing the string body in your loop. Create a temp variable in the loop and assign to that.
Related
I got stuck writing some simple program which writes some data to the text file and reads them form this file later.
I have a function that writes lines to a txt file; each line contains Name, Surname, and Idnumber.
And below I have a function that reads the data from that file.
I want to separate Name, Surname and Idnumber so below code seems to be correct but during debugging I got a message "An unhandled exception of type 'System.NullReferenceException' occurred" for this line:
string[] tabstring = myString.Split(' ', ' ');.
I created the tab string which contains 3 elements - each for each word in the line i.e. tabstring[0]=Name and so on.
The while loop is to do it for each line in the text file. But something is wrong.
public void ReadFromFile()
{
FileStream fsListOfObjects = new FileStream("C:\\Users\\Dom\\Desktop\\ListOfObjects.txt",
FileMode.Open);
StreamReader srListOfObjects = new StreamReader(fsListOfObjects);
while (srListOfObjects.ReadLine() != null)
{
string myString= (srListOfObjects.ReadLine();
Console.WriteLine(myString);
**string[] tabstring = myString.Split(' ', ' ');**
Name = tabstring[0];
Surname = tabstring[1];
Id= long.Parse(tabstring[2]);
ClassName object= new ClassName(Name, Surname, Id);
myList.Add(object);
}
srListOfObjects.Close();
Console.ReadLine();
}
And here is what the text file looks like:
Ann Brown 1233456789
Bruce Willis 098987875
Bill Gates 789678678
and so on...
I would appreciate your comments on the described problem.
while (srListOfObjects.ReadLine().. reads a line but doesn't save it into a variable. string myString= (srListOfObjects.ReadLine()) reads another line.
Use while (!srListOfObjects.EndOfStream) to check for the end of the stream: StreamReader.EndOfStream Property.
Also, it is a good idea to check that the correct number of parts of the string were obtained by the Split - it guards against things like lines with only whitespace.
Things like StreamReaders need have .Dispose() called on them to clear up "unmanaged resources" - an easy way to do that which will work even if the program crashes is to use the using statement.
If you make the ReadFromFile method into a function instead of a void then you can avoid (no pun) using a global variable for the data. Global variables are not necessarily a problem, but it's usually good to avoid them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApp1
{
public class ClassName
{
public string Name { get; set; }
public string Surname { get; set; }
public long Id { get; set; }
}
class Program
{
public static List<ClassName> ReadFromFile(string fileName)
{
var result = new List<ClassName>();
using (var sr = new StreamReader(fileName))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
var parts = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Count() == 3)
{
result.Add(new ClassName
{
Name = parts[0],
Surname = parts[1],
Id = long.Parse(parts[2])
});
}
}
}
return result;
}
static void Main(string[] args)
{
string myFile = #"C:\temp\namesList.txt";
var theList = ReadFromFile(myFile);
foreach(var c in theList)
{
Console.WriteLine($"{c.Id} - {c.Surname}, {c.Name}");
}
Console.ReadLine();
}
}
}
outputs:
1233456789 - Brown, Ann
98987875 - Willis, Bruce
789678678 - Gates, Bill
Your problem is here:
while (srListOfObjects.ReadLine() != null)
{
string myString= (srListOfObjects.ReadLine();
You are entering the loop on the condition that srListOfObjects.ReadLine() returns something other than null but then you are immediately reading a new line form srListOfObjects and storing the returned reference in myString. This has obviously two problems:
The second call to ReadLine can return null and you are not checking if it is. The error you are getting is due to this reason.
You are losing information. You are ignoring the line you are reading when checking the while condition. Until your program crashes or runs to the end (depends on wether the input file has even or odd number of lines), you will process only half of the data.
Update:
You should only read one line per iteration. One way to do it is declaring and initializing myString before entering the loop and updating it on every iteration:
var myString = srListOfObjects.ReadLine();
while (myString != null)
{
//do your stuff
myString = srListOfObjects.ReadLine();
}
https://learn.microsoft.com/en-us/dotnet/api/system.io.streamreader.readline?view=netcore-3.1
ReadLine() - Reads a line of characters from the current stream and returns the data as a string.
In your code you do a null check, but then call ReadLine again. When you hit the last line, you will get a NULL string, and splitting that will fail with the NULL ref
I'm currently writing some software where I have to load a lot of columnnames from an external file. Usually I would do this with some JSON but for reasons of userfriendlyness I can't do it right now. I need to use a textfile which is readable to the users and includes a lot of comments.
So I have created my own file to hold all of these values.
Now when I'm importing these values in my software I essentially run through my configfile line by line and I check for every line if it matches a parameter which I then parse. But this way I end up with a big codeblock with very repetitive code and I was wondering is could not simplify it in a way so that every check is done in just one line.
Here is the code I'm currently using:
if (line.Contains("[myValue]"))
{
myParameter = line.Replace("[myValue]", string.Empty).Trim();
}
I know that using Linq you can simply things and put them in one single line, I'm just not sure if it would work in this case?
Thanks for your help!
Kenneth
Why not just create a method if this piece of code often repeated :
void SetParameter(string line, string name, ref string parameter)
{
if (line.Contains(name))
{
parameter = line.Replace(name, string.Empty).Trim();
}
}
SetParameter(line, "[myValue]", ref myParameter);
If you want to avoid calling both Replace and Contains, which is probably a good idea, you could also just call Replace:
void SetParameter(string line, string name, ref string parameter)
{
var replaced = line.Replace(name, string.Empty);
if (line != replaced)
{
parameter = replaced.Trim();
}
}
Try this way (ternary):
myParameter = line.Contains("[myValue]")?line.Replace("[myValue]", string.Empty).Trim():myParameter;
Actually,
line.IndexOf should be faster.
From your code, look like you are replacing with just empty text, so why not take the entire string (consisting of many lines) and replace at one shot, instead of checking one line at a time.
You could use RegEx. This might possibly relieve you of some repetitive code
string line = "[myvalue1] some string [someotherstring] [myvalue2]";
// All your Keys stored at a single place
string[] keylist = new string[] { #"\[myvalue1]", #"\[myvalue2]" };
var newString = Regex.Replace(line, string.Join("|", keylist), string.Empty);
Hope it helps.
I want to add strings(abc, def,ghi) to a list of type string,
List<string> names = new List<string>();
Reading unknown number of lines till user enters an Enterkey
All the strings are added to the list
when I use a string(line in below example) and assign it the output of Console.ReadLine() and check if its Empty or not
string line;
while ((!string.IsNullOrEmpty(line =Console.ReadLine())))
{
names.Add(line); //This works abc, def,ghi are added to the list
}
But this doesn't work when I directly compare the Output of Console.ReadLine()
while ((!string.IsNullOrEmpty(Console.ReadLine())))
{
names.Add(Console.ReadLine()); //only def is added to the list
}
I could not identify the issue here.
Why not a simple infinite loop with break:
List<string> names = new List<string>();
while (true) {
string line = Console.ReadLine();
if (string.IsNullOrEmpty(line)) // break on Enter - i.e. on empty line
break;
names.Add(line); // otherwise add into the list
}
Consider what Console.ReadLine() does and how your loop works:
while ((!string.IsNullOrEmpty(Console.ReadLine()))) // read abc, but don't do anything with it
{
names.Add(Console.ReadLine()); // read def, add it to the list
}
Your loop is essentially skipping every other line of input, because each iteration of the loop reads two lines of input, but adds only one of those lines to the list. This is precisely why one would store that input in a variable:
string input = Console.ReadLine();
while (!string.IsNullOrEmpty(input))
{
names.Add(input);
input = Console.ReadLine();
}
(Or, if you prefer, the original example you posted. Though personally I find variable assignment in an operation like that to be distasteful, but that's a matter of personal preference.)
In the second example, you're still using names.Add(line) when line has no value. Also, while having the console take an input, that value isn't saved anywhere.
With your edit it takes two inputs instead of just one.
I need to find in a text everything that starts with [ and ends with ] and replace it with the value that a function returns. So, here is an example of what I'm doing:
public string ProcessTemplate(string input)
{
return Regex.Replace(input, #"\[(.*?)\]", new MatchEvaluator(delegate(Match match)
{
return ProcessBlock(match.Result("$1"));
}));
}
public string ProcessBlock(string value)
{
return Block.FromString(value).Process();
}
Now, my problem is when I need to edit blocks. So I thought to find blocks, edit them, and then replacing them in the text.
So, I created a List of blocks, and separated the ProcessTemplate method to two methods: FindBlocks and ReplaceBlocks:
public void FindBlocks(string input)
{
Input = input;
foreach (Match match in Regex.Matches(input, #"\[(.*?)\]"))
Blocks.Add(Block.FromString(match.Result("$1")));
}
public string ReplaceBlocks()
{
string input = Input;
foreach (Block block in Blocks)
input = input.Replace("[" + block.OrginalText + "]", block.Process);
return input;
}
public IList<Block> Blocks
{
get;
set;
}
public string Input
{
get;
set;
}
It's working, but the problem is that it is pretty slow. I measured with System.Diagnostics.Stopwatch every part, and I found out that the String.Replace in the ReplaceBlocks method is pretty slow.
How can I improve it?
Thanks.
replacing the string in ReplaceBlock with a StringBuilder may provide a performance increase as every time you perform a string.replace it will have to deallocate the string and reallocate the string. string builder doesnt need to do this.
Replace the contents of the ReplaceBlock with the following.
// This will require a reference to System.Text
StringBuilder input =new StringBuilder(Input);
foreach (Block block in Blocks)
{
input = input.Replace("[" + block.OrginalText + "]", block.Process);
}
return input.ToString();
I have also found replacing the foreach loops with a for loop which are quicker.
I think it is slow
Don't optimize until you've profiled. Find out why your code is slow, then optimize those parts.
http://c2.com/cgi/wiki?ProfileBeforeOptimizing
That's what I've written so far:
string omgwut;
omgwut = textBox1.Text;
omgwut = omgwut.Replace(" ", "snd\\space.wav");
omgwut = omgwut.Replace("a", "snd\\a.wav");
Now, the problem is that this code would turn
"snd\space.wav"
into
"snd\spsnd\a.wavce.wsnd\a.wavv"
in line four. Not what I'd want! Now I know I'm not good at C#, so that's why I'm asking.
Solutions would be great! Thanks!
You'll still need to write the getSoundForChar() function, but this should do what you're asking. I'm not sure, though, that what you're asking will do what you want, i.e., play the sound for the associated character. You might be better off putting them in a List<string> for that.
StringBuilder builder = new StringBuilder();
foreach (char c in textBox1.Text)
{
string sound = getSoundForChar( c );
builder.Append( sound );
}
string omgwut = builder.ToString();
Here's a start:
public string getSoundForChar( char c )
{
string sound = null;
if (sound == " ")
{
sound = "snd\\space.wav";
}
... handle other special characters
else
{
sound = string.Format( "snd\\{0}.wav", c );
}
return sound;
}
The problem is that you are doing multiple passes of the data. Try just stepping through the characters of the string in a loop and replacing each 'from' character by its 'to' string. That way you're not going back over the string and re-doing those characters already replaced.
Also, create a separate output string or array, instead of modifying the original. Ideally use a StringBuilder, and append the new string (or the original character if not replacing this character) to it.
I do not know of a way to simultaneously replace different characters in C#.
You could loop over all characters and build a result string from that (use a stringbuilder if the input string can be long). For each character, you append its replacement to the result string(builder).
But what are you trying to do? I cannot think of a useful application of appending file paths without any separator.