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
Related
Code:
using System;
using System.IO;
namespace TimeTress
{
class Program
{
static void Main(string[] args)
{
GetString("../../../../timeline.csv");
GetString("../../../../people.csv");
}
static void GetString(string path)
{
if (File.Exists(path))
{
foreach (var line in File.ReadAllLines(path))
{
Console.WriteLine(line);
}
}
else
{
Console.WriteLine($"Файл не найден по пути {Path.GetFullPath(path)}");
}
}
}
}
It is necessary that the result should not be simply displayed, but written into two different variables, preferably into the arrays string [] [] or string [], so that you can work with them in the future. File text: timeline: {event_date}; {event_description} people: {Name}; {Date of Birth}; {Date of death}
Change the method's return type to string[] or List<string> (if you don't know the difference, use Google)
then, instead of:
Console.WriteLine(line);
create a list and add lines to it:
List<string> result = new ListString();
foreach (var line in File.ReadAllLines(path))
{
result.Add(line);
}
return line; // or `lines.ToArray()`, if your return type is `string[]`
Now you need to think about the return value when the file does not exist. One option would be returning null. But that's not good and forces the users (of the method, I mean, the programmers) to check for null result whenever the method is called. A better option in my opinion is not checking for the existence of the file. Yes, you heard me right. Let the FileNotFoundException be thrown.
For more elaborate ways of processing the CSV file and parsing fields, consult other posts on the internet, including the ones available in StackOverflow
First, the variable(s) need to be defined before a value can be assigned to the variable(s). This variable definition is done using a statement similar to the following:
string[] stringArray;
After defining a variable, it is possible to assign values to the intended variable(s), as shown below:
stringArray = new string[] { "message one", "message two" };
Building upon this, if one would like to read through the lines in a text file, assign the lines to an array of strings, and then return a variable containing that array of strings, the following function would be one way to accomplish this:
static string[] GetListOfStringsFromTextFile(string filePath)
{
string[] stringArray;
if (File.Exists(filePath))
{
// ReadAllLines() already returns a string array. No need to loop.
stringArray = File.ReadAllLines(filePath);
}
else
{
// If file not found, return empty string array as default.
stringArray = new string[0];
}
return stringArray;
}
I have problem with looping code:
using System;
using System.Globalization;
namespace test
{
class Program
{
static void Main(string[] args)
{
string text = Console.ReadLine();
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
Console.WriteLine(ti.ToTitleCase(text).Replace(" ", string.Empty));
Console.ReadKey();
}
}
}
And maybe it's wrote like it shouldn't be because I can't find a way to fix it. To be specific I want this program to enter sentence in multiple lines of unknown number, it delete all white space and change every word first letter to upper case. So for example the enter data is:
I wanna ride bicycle,
but Rick say skateboard is better.
And output is:
IWannaRideBicycle,
ButRickSaySkateboardIsBetter.
The program can't have user interface so I think about while and making a list of strings but the problem for me will be still a way to loop it. I found a solution in C++ that they use "while ( getline (cin, text){}" but I think it's not useful in C#.
A while loop should do the trick. Console.ReadLine returns null if no more lines are available.
static void Main(string[] args)
{
List<string> converted = new List<string>();
while (true) // forever
{
string text = Console.ReadLine();
if (text == null)
{
break; // no more lines available - break out of loop.
}
// Convert to capitals
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
string convertedText = ti.ToTitleCase(text).Replace(" ", "");
converted.Add(convertedText);
}
// Now display the converted lines
foreach (string text in converted)
{
Console.WriteLine(text);
}
}
var text = Console.ReadLine();
while(!String.IsNullOrEmpty(text))
{
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
Console.WriteLine(ti.ToTitleCase(text).Replace(" ", string.Empty));
text = Console.ReadLine();
}
I think this may be a suitable solution. It will read input from the console until the user simply hits 'Enter' and sends you an empty string. I don't know that there is a more dynamic way to achieve what you're after.
It depends what your input is.
If its an IEnumerable<string> then simply use foreach:
var lines = ... //some IEnumerable<string>
foreach (var line in lines)
{
//do your thing
}
This will keep on looping as long as there is one more line to enumerate in lines. It can, in theory, keep on going forever.
If your input is a Stream then build a StreamReader around it and basically do the same (but with more plumbing):
using (var inputStream = ...// some stream)
using (var reader = new StreamReader(inputStream))
{
var line = reader.ReadLine();
while (line != null)
{
//do your thing
line = reader.ReadLine();
}
}
This again will loop as long as the input stream can produce a new line.
I'm trying to get certain strings out of a text file and put it in a variable.
This is what the structure of the text file looks like keep in mind this is just one line and each line looks like this and is separated by a blank line:
Date: 8/12/2013 12:00:00 AM Source Path: \\build\PM\11.0.64.1\build.11.0.64.1.FileServerOutput.zip Destination Path: C:\Users\Documents\.NET Development\testing\11.0.64.1\build.11.0.55.5.FileServerOutput.zip Folder Updated: 11.0.64.1 File Copied: build.11.0.55.5.FileServerOutput.zip
I wasn't entirely too sure of what to use for a delimiter for this text file or even if I should be using a delimiter so it could be subjected to change.
So just a quick example of what I want to happen with this, is I want to go through and grab the Destination Path and store it in a variable such as strDestPath.
Overall the code I came up with so far is this:
//find the variables from the text file
string[] lines = File.ReadAllLines(GlobalVars.strLogPath);
Yeah not much, but I thought perhaps if I just read one line at at a time and tried to search for what I was looking for through that line but honestly I'm not 100% sure if I should stick with that way or not...
If you are skeptical about how large your file is, you should come up using ReadLines which is deferred execution instead of ReadAllLines:
var lines = File.ReadLines(GlobalVars.strLogPath);
The ReadLines and ReadAllLines methods differ as follows:
When you use ReadLines, you can start enumerating the collection of strings before the whole collection is returned; when you use ReadAllLines, you must wait for the whole array of strings be returned before you can access the array. Therefore, when you are working with very large files, ReadLines can be more efficient.
As weird as it might sound, you should take a look to log parser. If you are free to set the file format you could use one that fits with log parser and, believe me, it will make your life a lot more easy.
Once you load the file with log parse you can user queries to get the information you want. If you don't care about using interop in your project you can even add a com reference and use it from any .net project.
This sample reads a HUGE csv file a makes a bulkcopy to the DB to perform there the final steps. This is not really your case, but shows you how easy is to do this with logparser
COMTSVInputContextClass logParserTsv = new COMTSVInputContextClass();
COMSQLOutputContextClass logParserSql = new COMSQLOutputContextClass();
logParserTsv.separator = ";";
logParserTsv.fixedSep = true;
logParserSql.database = _sqlDatabaseName;
logParserSql.server = _sqlServerName;
logParserSql.username = _sqlUser;
logParserSql.password = _sqlPass;
logParserSql.createTable = false;
logParserSql.ignoreIdCols = true;
// query shortened for clarity purposes
string SelectPattern = #"Select TO_STRING(UserName),TO_STRING(UserID) INTO {0} From {1}";
string query = string.Format(SelectPattern, _sqlTable, _csvPath);
logParser.ExecuteBatch(query, logParserTsv, logParserSql);
LogParser in one of those hidden gems Microsoft has and most people don't know about. I have use to read iis logs, CSV files, txt files, etc. You can even generate graphics!!!
Just check it here http://support.microsoft.com/kb/910447/en
Looks like you need to create a Tokenizer. Try something like this:
Define a list of token values:
List<string> gTkList = new List<string>() {"Date:","Source Path:" }; //...etc.
Create a Token class:
public class Token
{
private readonly string _tokenText;
private string _val;
private int _begin, _end;
public Token(string tk, int beg, int end)
{
this._tokenText = tk;
this._begin = beg;
this._end = end;
this._val = String.Empty;
}
public string TokenText
{
get{ return _tokenText; }
}
public string Value
{
get { return _val; }
set { _val = value; }
}
public int IdxBegin
{
get { return _begin; }
}
public int IdxEnd
{
get { return _end; }
}
}
Create a method to Find your Tokens:
List<Token> FindTokens(string str)
{
List<Token> retVal = new List<Token>();
if (!String.IsNullOrWhitespace(str))
{
foreach(string cd in gTkList)
{
int fIdx = str.IndexOf(cd);
if(fIdx > -1)
retVal.Add(cd,fIdx,fIdx + cd.Length);
}
}
return retVal;
}
Then just do something like this:
foreach(string ln in lines)
{
//returns ordered list of tokens
var tkns = FindTokens(ln);
for(int i=0; i < tkns.Length; i++)
{
int len = (i == tkns.Length - 1) ? ln.Length - tkns[i].IdxEnd : tkns[i+1].IdxBegin - tkns[i].IdxEnd;
tkns[i].value = ln.Substring(tkns[i].IdxEnd+1,len).Trim();
}
//Do something with the gathered values
foreach(Token tk in tkns)
{
//stuff
}
}
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.
I need to read text data from file, where there are different types of data in every line.
So, I created a one big class named subject. My data looks something like this:
Subject name M1 M2 M3 M4
Subject1 5 7 8 3
Old Subject 1 2 5 9
The main question is, if it is possible to read all the data in line 1 for instance and assign it to proper fields, like SubjName = Subject1, M1 = 5, M2 = 7, M3 = 8 and so on, WITHOUT using substrings? (something like stream >> Subject.SubjName; stream >> Subject.M1 = 5 and so on in C++).
Here's the code that I have.
internal void Read()
{
TextReader tr = new StreamReader("Data.txt");
string line;
while ((line = tr.ReadLine()) != null) //read till end of line
{
tr.ReadLine(); //Skips the first line
}
Thanks in advance
EDIT: To clarify, I'd prefer that fields are delimited.
Something like the solution in this question might help, but obviously use a tab delimeter (\t)
CSV to object model mapping
from line in File.ReadAllLines(fileName).Skip(1)
let columns = line.Split(',')
select new
{
Plant = columns[0],
Material = int.Parse(columns[1]),
Density = float.Parse(columns[2]),
StorageLocation = int.Parse(columns[3])
}
It is not clear from your question how the records are stored in the file - whether fields are delimited or fixed length.
Regardless - you can use the TextFieldParser class, which:
Provides methods and properties for parsing structured text files.
It lives in the Microsoft.VisualBasic.FileIO namespace in the Microsoft.VisualBasic.dll assembly.
Split and Dictionary and your two methods of choice here. You read in your line, split it by empty spaces, then save it as a name/object pair in a dictionary.
Put the code below into a *.cs file, then build and run it as a demo:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;
namespace stringsAsObjects
{
class stringObject
{
public static int Main(string[] args)
{
int counter = 0;
string line;
// Read the file and display it line by line.
System.IO.StreamReader file =
new System.IO.StreamReader("Data.txt");
string nameLine = file.ReadLine();
string valueLine = file.ReadLine();
file.Close();
string[] varNames = nameLine.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);
string[] varValues = valueLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
Dictionary<string, object> map = new Dictionary<string, object>();
for(int i = 0; i<varNames.Length; i++)
{
try
{
map[varNames[i]] = varValues[i];
}
catch (Exception ex)
{
map[varNames[i]] = null;
}
}
foreach (object de in map)
{
System.Console.WriteLine(de);
}
Console.ReadKey();
return 0;
}
}
}