0

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.

Andrew Morton
  • 21,016
  • 8
  • 48
  • 69
kalka79
  • 1
  • 3
  • Just to be precise : each line of text file contains just three words Name (space between) Surname(spacebetween) Idnumber. – kalka79 May 05 '20 at 10:32
  • Note: the file might have tabs in between and this is considered a different character. Better post the file contents as is – Bill Togkas May 05 '20 at 10:33
  • Does this answer your question? [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – Pavel Anikhouski May 05 '20 at 10:34
  • No the file does nto any tabs between becouse it is created by function, there is only a space between each word, – kalka79 May 05 '20 at 10:35

3 Answers3

0

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:

  1. 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.
  2. 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();
}
InBetween
  • 30,991
  • 3
  • 46
  • 80
  • Yes ! exactly your number 2 describes exactly what I got - the results contains just every second line nad then the program crushes. So what is the solution then ? Should I insert some checks by "if" which check if the line is in fact not null and then it can be assigned to the string my String ?? – kalka79 May 05 '20 at 10:49
0

https://docs.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

Matt Evans
  • 6,601
  • 7
  • 29
  • 60
  • as @inbetween points out, you are also progressing past your first line without processing it – Matt Evans May 05 '20 at 10:38
  • It is all true but the loop while should stop once it gets to an emty line that means to the end of the file thus the string myString= (srListOfObjects.ReadLine(); should not be executed , am I right? – kalka79 May 05 '20 at 10:45
  • @kalka79 no. Your file has one single line. Inside the wile loop condition, your read a line, and its not null. You enter the loop body. Now you read a new line and you store it in `myString`. But the file only has one line, so the new call to read line return `null` and `myString` is therefore `null`. You then write `myString` to the Console. Writing `null` to the console is not an error, it will simply print the same as `Console.WriteLine()`. You then try to acess an instance member of `myString` calling `.Split` and there is where your program crashes. – InBetween May 05 '20 at 10:49
  • This file contains a couple of lines not one. But the line which is inside the while loop condition ahould be the same line which is stored in myString right? – kalka79 May 05 '20 at 10:53
  • @kalka79 It doesn't matter, it will crash if the total number of lines is an odd number. If the total number of lines is an even number, it wont crash which is worse becuase you'll think its working fine and it really isn't; your are losing 50% of the information you are trying to process. – InBetween May 05 '20 at 10:54
  • Yes I jsut noticed that if I have more lines in the file then I got something like every second line as an output - but I still can't understand why? – kalka79 May 05 '20 at 10:56
  • @kalka79 see my answer. Count how many times you are calling `ReadLine` per loop cycle and compare it to the amount of times you should be calling it. If you want to process one line per loop, you should call `ReadLine` only once, but you are calling it twice... – InBetween May 05 '20 at 11:02
0

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

Andrew Morton
  • 21,016
  • 8
  • 48
  • 69