Runtime error in StringBuilder instance - c#

Please, help me understand, what's wrong with this code. (I am trying to build a string, taking parts of it line by line from a text file).
I get a runtime error "In the instance of the object reference not set to an object" on the line strbuild.Append(str);
StreamReader reader = new StreamReader("buf.txt", System.Text.Encoding.ASCII);
StringBuilder strbuild = new StringBuilder();
strbuild = null;
while (reader.Peek() >= 0)
{
string str = null;
str = reader.ReadLine().ToString();
string segment = str.Substring(0, 1);
if (segment == "A")
{
strbuild.Append(str); //here i get an error
}
else if (segment == "B")
{
strbuild.Append("BET");
}
}
printstr = strbuild.ToString();
reader.Close();
MessageBox.Show(printstr);

Look at these lines:
StringBuilder strbuild = new StringBuilder();
strbuild = null;
What do you expect to happen when you then call strbuild.Append(...)? Why are you setting strbuild to null at all?
You seem to be fond of two-line variable initialization - here's another example:
string str = null;
str = reader.ReadLine().ToString();
This would be more easily readable as just:
string str = reader.ReadLine();
(ReadLine already returns a string, so you don't need to call ToString() on the result.)
However, I do suggest that you use a using statement for the StreamReader - otherwise when an exception is thrown, you'll be leaving the reader open.
One nice thing about TextReader.ReadLine() is that it returns null when you're done. You don't need to peek and then read.
Finally, if you're only testing a single character you don't need a substring - just use the string indexer to get a char. So, you could have:
StringBuilder builder = new StringBuilder();
// Consider using File.OpenText
using (StreamReader reader = new StreamReader("buf.txt", Encoding.ASCII))
{
string line;
// Normally side-effect + test is ugly, but this is a common and
// neat idiom
while ((line = reader.ReadLine()) != null)
{
// TODO: What do you want to happen for empty lines?
char segment = str[0];
if (segment == 'A')
{
builder.Append(line);
}
else if (segment == 'B')
{
builder.Append("BET");
}
}
}
MessageBox.Show(builder.ToString());

You are setting the stringbuilder to null after initialization.
Change
StringBuilder strbuild = new StringBuilder();
strbuild = null;
to
StringBuilder strbuild = new StringBuilder();
leaving out the line
strbuild = null;

Change
StringBuilder strbuild = new StringBuilder();
strbuild = null;
to
StringBuilder strbuild = null;
strbuild = new StringBuilder();
or, to prevent this kind of error:
StringBuilder strbuild = new StringBuilder();

There are some many errors within your example, here is a first corrected version:
StringBuilder strbuild = new StringBuilder();
// Put resource into using statements, for deterministic cleanup
using (TextReader reader = new StreamReader("buf.txt", System.Text.Encoding.ASCII))
{
string line;
//Maybe looks a little ugly the first time, but is commonly used to
//process all the lines of a file (.ReadToEnd() can cause memory problems
//on really big files)
while ((line = reader.ReadLine()) != null)
{
//Instead of if, else if, else if, etc just take a switch
//statement. Makes it much easier to read.
switch (line[0])
{
//If you need case insensitivity put both versions of the character
//before your first line of code
case 'A':
case 'a':
strbuild.Append(line);
break;
//Otherwise just use the lower or upper case version you like
case 'B':
strbuild.Append("BET");
break;
}
}
}
//Put the result of the StringBuilder directly into the Show() function
MessageBox.Show(strbuild.ToString());

Related

How to terminate reading from text file when file is empty

When I run my program and there is data in my text file it works as I intended. However, the program crashes if the text file is empty. Therefore, I was wondering how I remedy this issue.
Here is my code for reading from text file:
//This method reads the order data from a text file and assigns the values to each object's variables
void Read_Order_Data()
{
FileStream fs = new FileStream("i:\\OrderData.txt", FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(fs);
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
string[] fields = line.Split('#');
Order[Number_Of_Orders].Order_Number = fields[0];
Order[Number_Of_Orders].Type_Of_Bean = fields[1];
Order[Number_Of_Orders].Quantity_Of_Order = fields[2];
Order[Number_Of_Orders].Date_Of_Purchase = fields[3];
Number_Of_Orders++;
}//end of while statement
reader.Close();
}//end of Read_Order_Data()
If the text file is empty my program stops on this line:
Order[Number_Of_Orders].Type_Of_Bean = fields[1];
When it stops it says Type_Of_Bean is null.
Thanks for any assistance you can offer.
Change your while loop a bit:
string line;
while ((line = reader.ReadLine()) != null)
{
string[] fields = line.Split('#');
...
}
line will be null if the end of the input stream has been reached.
A simpler option may be to use Peek(). I modified the MSDN's example for StreamReader a tiny bit:
string line;
while (reader.Peek() >= 0)
{
line = reader.ReadLine();
...
}
Peek() will return -1 if there are no characters to be read (empty file).
And as others have suggested, make sure your split string array result has the number of fields you plan on indexing. If it doesn't, it might suggest a problem with the file you're reading.
Replace This:
string[] fields = line.Split('#');
With This:
string[] fields = line.Split(new []{'#'},StringSplitOptions.RemoveEmptyEntries);
You should add checking for length of fields in your code, like this:
...
string[] fields = line.Split(new[] {'#'}, StringSplitOptions.RemoveEmptyEntries);
if (fields.Length < 4) break;
...
you can check if field have the correct length
void Read_Order_Data()
{
FileStream fs = new FileStream("i:\\OrderData.txt", FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(fs);
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
string[] fields = line.Split('#');
if (fields.length == 4){
Order[Number_Of_Orders].Order_Number = fields[0];
Order[Number_Of_Orders].Type_Of_Bean = fields[1];
Order[Number_Of_Orders].Quantity_Of_Order = fields[2];
Order[Number_Of_Orders].Date_Of_Purchase = fields[3];
Number_Of_Orders++;
}
}//end of while statement
reader.Close();
}//end of Read_Order_Data()
You can also do
string[] lines = System.IO.File.ReadAllLines("File");
foreach (var line in lines)
{
string[] fields = line.Split('#');
}
If you file is empty, then the following line
string[] fields = line.Split('#');
will give you a string array with only one element (empty string).
Which means that when trying to access the second element (fields[1]), your code will raise an exception.
Short story: you should check the size of the fields array before accessing it.
You can enumerate a line at a time without having to buffer the entire contents like so:
foreach (string line in File.ReadLines("i:\\OrderData.txt"))
{
string[] fields = line.Split('#');
if (fields.Length < 4) // You might also need to add this.
continue;
...
If the file is empty, it will not even enter the body of the loop. It also does not make a copy of the entire file in memory - only one line is buffered in memory at once.

Extract data from text file

I need to extract some data from a text file and insert to columns in excel sheet. I know how to do this if the rows and the length of the string is known.
try
{
using (System.IO.StreamReader sr = new System.IO.StreamReader("test.txt")
{
string line;
while ((line = sr.ReadLine()) != null)
{
listSNR.Items.Add(line.Substring (78,4));
}
}
}
But the particular text file is complex and the starting index or the length cannot be provided. But the starting word (PCPU01) of the row is known.
Eg: PCPU01,T2716,0.00,0.01,0.00,0.00
output:
T2716 0 0.01 0 0
In that case can somebody please let me know how to extract the texts?
using(System.IO.StreamReader sr = new System.IO.StreamReader("test.txt"))
{
string line;
while((line = sr.ReadLine()) != null)
{
string[] split = line.Split(',');
//...
}
}
split[0] will return "PCPU01", split[1] "T2716" and so on.
You can split one string into an array of strings, separated by a given character. This way, you could split the source string by a comma and use the resulting strings to build your output. Example:
string source = "PCPU01,T2716,0.00,0.01,0.00,0.00";
string[] parts = source.Split(',');
StringBuilder result = new StringBuilder();
result.Append(parts[1]); // The second element in the array, i.e. T2716
result.Append(" ");
result.Append(parts[2]); // 0.00
... // And so on...
return result.ToString() // return a string, not a StringBuilder
I hope this helps a little bit. You might have to tweak it to your needs. But this is a higher level code that gives you general idea of extracting data off a notepad.
DialogResult result = openFileDialog.ShowDialog();
Collection<Info> _infoCollection = new Collection<Info>();
Collection<string> listOfSubDomains = new Collection<string>();
string[] row;
string line;
// READ THE FILE AND STORE IT IN INFO OBJECT AND STORE TAHT INFO OBJECT IN COLLECTION
try
{
using (StreamReader reader = new StreamReader(openFileDialog.FileName))
{
while((line = reader.ReadLine()) != null)
{
Info _info = new Info();
row = line.Split(' ');
_info.FirstName = row[0];
_info.LastName = row[1];
_info.Email = row[2];
_info.Id = Convert.ToInt32(row[3]);
_infoCollection.Add(_info);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
thanks for the answers. What i wanted is to identify the particular line in the text file and split the line into columns. So i was able to do this by calling a GetLine method:
string line15=GetLine(#"test.txt",15);
public string GetLine(string fileName, int line)
{
using (System.IO.StreamReader ssr = new System.IO.StreamReader("test.txt"))
//using (var ssr = new StreamReader("test.txt"))
{
for (int i = 1; i < line; i++)
ssr.ReadLine();
return ssr.ReadLine();
}
}
Then i splitted this line by using the delimiter (,)
This was my approach in C#. It takes a string input (which you can get out of a text file) and an int with which line you want to get. It then separates the string at a given seperator char to a list which in turn is then read out. If the given line number is lower than the count of the created list, the entry is given back.
public string GetLine(string multiline,int line)
{
List<string> lines = new List<string>();
lines = multiline.Split('\n').ToList<string>();
return lines.Count >= line ? lines[line] : "";
}

Reading and changing a file

I'm reading a file using C# and class TextReader
TextReader reader = new StreamReader(stream);
string line;
while ((line = reader.ReadLine()) != null)
{
if (someCondition)
{
// I want to change "line" and save it into the file I'm reading from
}
}
In a code there is a question: how do I save a changed line to a file I'm reading from and continue reading?
Fast and dirty solution would be:
TextReader reader = new StreamReader(stream);
string line;
StringBuilder sb = new StringBuilder();
while ((line = reader.ReadLine()) != null)
{
if (someCondition)
{
//Change variable line as you wish.
}
sb.Append(line);
}
using (StreamWriter sw = new StreamWriter("filePath"))
{
sw.Write(sb.ToString());
}
or
TextReader reader = new StreamReader(stream);
string line;
String newLines[];
int index = 0;
while ((line = reader.ReadLine()) != null)
{
if (someCondition)
{
//Change variable line as you wish.
}
newLines[index] = line;
index++;
}
using (StreamWriter sw = new StreamWriter("filePath"))
{
foreach (string l in newLines)
{
sw.WriteLine(l);
}
}
If memory is too important you can try this too:
TextReader reader = new StreamReader(stream);
string line;
while ((line = reader.ReadLine()) != null)
{
if (someCondition)
{
//Change variable line as you wish.
}
using (StreamWriter sw = new StreamWriter("filePath"))
{
sw.WriteLine(line);
}
}
The easiest thing is to write a new file, then when finished, replace the old file with the new file. This way you only do writes in one file.
If you try to read/write in the same file, you will run into problems when the content you want to insert is not the exact same size as the content it is replacing.
There is nothing magic about text files. They are just a stream of bytes representing characters in a text encoding. There are no line concept in the file, just separators in the form of newline characters.
If the file is not too large, you should simply rewrite the whole file:
var lines = File.ReadAllLines(path)
.Where(l => someCondition);
File.WriteAllLines(path, lines);
A very simple solution
void Main()
{
var lines = File.ReadAllLines("D:\\temp\\file.txt");
for(int x = 0; x < lines.Length; x++)
{
// Of course this is an example of the condtion
// you should implement your checks
if(lines[x].Contains("CONDITION"))
{
lines[x] = lines[x].Replace("CONDITION", "CONDITION2");
}
}
File.WriteAllLines("D:\\temp\\file.txt", lines);
}
The drawback is the memory usage caused by the in memory lines, but, if we stay around 50MB, it should be handled effortlessly by modern PC.

Reading a null-terminated string

I am reading strings from a binary file. Each string is null-terminated. Encoding is UTF-8. In python I simply read a byte, check if it's 0, append it to a byte array, and continue reading bytes until I see a 0. Then I convert byte array into a string and move on. All of the strings were read correctly.
How can I read this in C#? I don't think I have the luxury of simply appending bytes to an array since the arrays are fixed size.
Following should get you what you are looking for. All of text should be inside myText list.
var data = File.ReadAllBytes("myfile.bin");
List<string> myText = new List<string>();
int lastOffset = 0;
for (int i = 0; i < data.Length; i++)
{
if (data[i] == 0)
{
myText.Add(System.Text.Encoding.UTF8.GetString(data, lastOffset, i - lastOffset));
lastOffset = i + 1;
}
}
I assume you're using a StreamReader instance:
StringBuilder sb = new StringBuilder();
using(StreamReader rdr = OpenReader(...)) {
Int32 nc;
while((nc = rdr.Read()) != -1) {
Char c = (Char)nc;
if( c != '\0' ) sb.Append( c );
}
}
You can either use a List<byte>:
List<byte> list = new List<byte>();
while(reading){ //or whatever your condition is
list.add(readByte);
}
string output = Encoding.UTF8.GetString(list.ToArray());
Or you could use a StringBuilder :
StringBuilder builder = new StringBuilder();
while(reading){
builder.Append(readByte);
}
string output = builder.ToString();
If your "binary file" only contains null terminated UTF8 strings, then for .NET it isn't a "binary file" but just a text file because null characters are characters too. So you could just use a StreamReader to read the text and split it on the null characters.
(Six years later "you" would presumably be some new reader and not the OP.)
A one line (ish) solution would be:
using (var rdr = new StreamReader(path))
return rdr.ReadToEnd().split(new char[] { '\0' });
But that will give you a trailing empty string if the last string in the file was "properly" terminated.
A more verbose solution that might perform differently for very large files, expressed as an extension method on StreamReader, would be:
List<string> ReadAllNullTerminated(this System.IO.StreamReader rdr)
{
var stringsRead = new System.Collections.Generic.List<string>();
var bldr = new System.Text.StringBuilder();
int nc;
while ((nc = rdr.Read()) != -1)
{
Char c = (Char)nc;
if (c == '\0')
{
stringsRead.Add(bldr.ToString());
bldr.Length = 0;
}
else
bldr.Append(c);
}
// Optionally return any trailing unterminated string
if (bldr.Length != 0)
stringsRead.Add(bldr.ToString());
return stringsRead;
}
Or for reading just one at a time (like ReadLine)
string ReadNullTerminated(this System.IO.StreamReader rdr)
{
var bldr = new System.Text.StringBuilder();
int nc;
while ((nc = rdr.Read()) > 0)
bldr.Append((char)nc);
return bldr.ToString();
}

Using file.Close() with StreamWriter

I am having problems trying to use the file.Close with StreamWriter in this method, it doesn't seem to like it. Could someone demonstrate how this could be done. (The reason so, is that another method accesses a file being used and hence can't, because the file is still in use by the other method.)
Code so far:
private static void TrimColon()
{
using (StreamWriter sw = File.AppendText(#"process_trimmed.lst"))
{
StreamReader sr = new StreamReader(#"process_trim.lst");
string myString = "";
while (!sr.EndOfStream)
{
myString = sr.ReadLine();
int index = myString.LastIndexOf(":");
if (index > 0)
myString = myString.Substring(0, index);
sw.WriteLine(myString);
}
}
}
private static void TrimColon(String inputFilePath, String outputFilePath)
{
//Error checking file paths
if (String.IsNullOrWhiteSpace(inputFilePath))
throw new ArgumentException("inputFilePath");
if (String.IsNullOrWhiteSpace(outputFilePath))
throw new ArgumentException("outputFilePath");
//Check to see if files exist? - Up to you, I would.
using (var streamReader = File.OpenText(inputFilePath))
using (var streamWriter = File.AppendText(outputFilePath))
{
var text = String.Empty;
while (!streamReader.EndOfStream)
{
text = streamReader.ReadLine();
var index = text.LastIndexOf(":");
if (index > 0)
text = text.Substring(0, index);
streamWriter.WriteLine(text);
}
}
}
The StreamWriter is closed aswell as flushed due to the "using" statement. So no need to call close.

Categories

Resources