Sign in to follow this  
Brauss

[Visual C#] The Hauntings of NullArgumentException

Recommended Posts

I have been haunted by the ghost of NullArgumentException. Basically the program I am making is a game menu. It has functions to take a new username and password and write them to files to save them for the next running of the program. However, when I try and run the program when there are no current usernames or passwords, it fails because the file is null(?). This is the function which results in the NullArgumentException: (three blocks of code - one for each character slot)
// ----------------------- SHOW THE NAMES
    static void ShowNames()
    {
        System.IO.StreamReader reader;        

        reader = new System.IO.StreamReader(name1);
        string str1 = reader.ReadLine();
        char[] char1 = str1.ToCharArray();
        if (char1.Length != 0)
            System.Console.WriteLine("| 1. ", str1, "|");
        else System.Console.WriteLine("| 1. Empty slot        |");


        reader = new System.IO.StreamReader(name2);
        string str2 = reader.ReadLine();
        char[] char2 = str1.ToCharArray();
        if (char1.Length != 0)
            System.Console.WriteLine("| 1. ", str2, "|");
        else System.Console.WriteLine("| 1. Empty slot        |");


        reader = new System.IO.StreamReader(name3);
        string str3 = reader.ReadLine();
        char[] char3 = str1.ToCharArray();
        if (char1.Length != 0)
            System.Console.WriteLine("| 1. ", str3, "|");
        else System.Console.WriteLine("| 1. Empty slot        |");

    }



What the function is meant to do is read all the three username files, and then convert the string result into an array of characters. Then, if the length of the array is 0, I assume no username has been set, so it prints empty slot. Otherwise it prints the string from the file. I have been told that it can be solved by declaring new readers at the top, but this didn't work and kinda resulted in more complications. Here is the whole program (228 lines) if you need it:
// ------------------------------------------------------------------------ CLASS
public class Game
{
    static string[] characters = new string[3];
    static string[] full_characters = new string[3];
    static string[] passwords = new string[3];

    static bool[] ischaracter = new bool[3];
    static bool[] ispassword = new bool[3];

    static System.IO.FileStream name1, name2, name3;
    static System.IO.FileStream pass1, pass2, pass3;



    // --------------------------------------------------------------------- MAIN 
    static void Main(string[] args)
    {

        // ----------------------- START MENU
        System.Console.WriteLine("Welcome to Odyssey!\n");
        System.Console.WriteLine("Choose your character:\n\n");
        System.Console.WriteLine("---------------------");
        System.Console.WriteLine("| Character name    | ");
        System.Console.WriteLine("|-------------------| ");

        Game.ShowNames();

        System.Console.WriteLine("---------------------");


        // ----------------------- TAKE CHARACTER CHOICE AND RESPOND
        string choice = System.Console.ReadLine();
        int choicenum = 0;

        if (choice == "1") choicenum = 1;
        if (choice == "2") choicenum = 2;
        if (choice == "3") choicenum = 3;

        Game.RespondChoice(choicenum);


        // ---------------------- SET NEW PASSWORD


        // ---------------------- TAKE PASSWORD ATTEMPT AND CHECK IT
        string pass_try = System.Console.ReadLine();

        Game.CheckPass(choicenum);
    }

    // ---------------------------------------------------------------- FUNCTIONS

    // ----------------------- SHOW THE NAMES
    static void ShowNames()
    {
        System.IO.StreamReader reader;        

        reader = new System.IO.StreamReader(name1);
        string str1 = reader.ReadLine();
        char[] char1 = str1.ToCharArray();
        if (char1.Length != 0)
            System.Console.WriteLine("| 1. ", str1, "|");
        else System.Console.WriteLine("| 1. Empty slot        |");


        reader = new System.IO.StreamReader(name2);
        string str2 = reader.ReadLine();
        char[] char2 = str1.ToCharArray();
        if (char1.Length != 0)
            System.Console.WriteLine("| 1. ", str2, "|");
        else System.Console.WriteLine("| 1. Empty slot        |");


        reader = new System.IO.StreamReader(name3);
        string str3 = reader.ReadLine();
        char[] char3 = str1.ToCharArray();
        if (char1.Length != 0)
            System.Console.WriteLine("| 1. ", str3, "|");
        else System.Console.WriteLine("| 1. Empty slot        |");

    }

    // ----------------------- FILL THE NAMES TO WIDTH 15
    static void FillChars()
    {
        full_characters[0] = characters[0].PadRight(15);
        full_characters[1] = characters[1].PadRight(15);
        full_characters[2] = characters[2].PadRight(15);
    }

    // ---------------------- RESPOND TO CHARACTER CHOICE
    static void RespondChoice(int i)
    {
        switch (i)
        {
            case 1:
                if (ischaracter[1]) Game.CheckPass(i);
                else Game.NewName(1);
                break;

            case 2:
                if (ischaracter[1]) Game.CheckPass(i);
                else Game.NewName(2);
                break;

            case 3:
                if (ischaracter[2]) Game.CheckPass(i);
                else Game.NewName(3);
                break;
        }
    }
    
    // ---------------------- SET NEW CHARACTER NAME
    static void NewName(int i)
    {
        System.Console.WriteLine("Choose your character's name.\n");

        System.IO.StreamWriter writer;

        switch (i)
        {
            case 1:
                name1 = System.IO.File.OpenWrite("name1.txt");
                writer = new System.IO.StreamWriter(name1);

                characters[0] = System.Console.ReadLine();
                writer.WriteLine(characters[0]);
                writer.Close();
                ischaracter[0] = true;
                break;
            case 2:
                name2 = System.IO.File.OpenWrite("name2.txt");
                writer = new System.IO.StreamWriter(name2);

                characters[1] = System.Console.ReadLine();
                writer.WriteLine(characters[1]);
                writer.Close();
                ischaracter[1] = true;
                break;
            case 3:
                name3 = System.IO.File.OpenWrite("name3.txt");
                writer = new System.IO.StreamWriter(name3);

                characters[2] = System.Console.ReadLine();
                writer.WriteLine(characters[2]);
                writer.Close();
                ischaracter[2] = true;
                break;
        }

        Game.NewPass(i);
    }
    
    // ---------------------- SET NEW PASSWORD
    static void NewPass(int i)
    {

        System.Console.WriteLine("Choose your character's password.\n");

        System.IO.StreamWriter writer;

        switch (i)
        {
            case 1:
                pass1 = System.IO.File.OpenWrite("pass1.txt");
                writer = new System.IO.StreamWriter(pass1);

                passwords[0] = System.Console.ReadLine();
                writer.WriteLine(passwords[0]);
                writer.Close();
                System.Console.WriteLine("You're in.");
                break;
            case 2:
                pass2 = System.IO.File.OpenWrite("pass2.txt");
                writer = new System.IO.StreamWriter(pass2);

                passwords[1] = System.Console.ReadLine();
                writer.WriteLine(passwords[1]);
                writer.Close();
                System.Console.WriteLine("You're in.");
                break;
            case 3:
                pass3 = System.IO.File.OpenWrite("pass3.txt");
                writer = new System.IO.StreamWriter(pass3);

                passwords[3] = System.Console.ReadLine();
                writer.WriteLine(passwords[3]);
                writer.Close();
                System.Console.WriteLine("You're in.");
                break;
        }
    }
    // --------------------- CHECK PASSWORD IS CORRECT
    static void CheckPass(int i)
    {
        string[] attempts = new string[3];

        System.Console.WriteLine("Enter your password:\n");

        System.IO.StreamReader reader;

        switch (i)
        {
            case 1:
                reader = new System.IO.StreamReader(pass1);
                attempts[0] = System.Console.ReadLine();
                if (attempts[0] == reader.ReadLine())
                    System.Console.WriteLine("You're in.");
                else System.Console.WriteLine("Incorrect password.");
                break;
            case 2:
                reader = new System.IO.StreamReader(pass2);
                attempts[1] = System.Console.ReadLine();
                if (attempts[1] == reader.ReadLine())
                    System.Console.WriteLine("You're in.");
                else System.Console.WriteLine("Incorrect password.");
                break;
            case 3:
                reader = new System.IO.StreamReader(pass3);
                attempts[2] = System.Console.ReadLine();
                if (attempts[2] == reader.ReadLine())
                    System.Console.WriteLine("You're in.");
                else System.Console.WriteLine("Incorrect password.");
                break;
        }
    }
}



Any help is greatly appreciated.

Share this post


Link to post
Share on other sites
We told you in IRC that you need to instantiate your FileStream before you pass it to the StreamReader constructor, which is what is throwing the ArgumentNullException. For example, FileStream name1 = new FileStream(...); Of course, you'll need to insert the appropriate parameters there.

Share this post


Link to post
Share on other sites
I thought that was what I had already done at the top of the screen?

With


static System.IO.FileStream name1, name2, name3;



?

Also, I am not trying to read bytes or anything like that. I have never used the constructor with 4 parameters before and have read and written to files fine. I tried adding it at the top but I got all the parameters wrong and it didn't seem to work.

Surely this is not the easiest way? I don't want to read bytes. Just strings.

I made a program before which read and wrote to a file without doing that. =S

Share this post


Link to post
Share on other sites

static System.IO.FileStream name1, name2, name3;



only creates the variables. It doesn't create the object. Its the same as writing


static System.IO.FileStream name1 = null, name2 = null, name3 = null;


Share this post


Link to post
Share on other sites
Yeah, I remember now.

I added this to the start of each of the three blocks:


name1 = System.IO.File.OpenWrite("name1.txt");



but now when I run the program it has an error on the same line as before:


reader = new System.IO.StreamReader(name1);



and it says "Stream was not readable.". Now the error is ArgumentException.

=l

Share this post


Link to post
Share on other sites
OK, I changed it, but now it says 'Object reference not set to an instance of an object' and it is NullReferenceException.

I think this would be fixed if I could somehow check if the file contains any information instead of reading it and putting it into a char[] and then checking if that is empty.

But I searched the documentation and I only found a way to search whole directories, suffice it to say it did not work.


Share this post


Link to post
Share on other sites
First thing you could do is see if the file exists using System.IO.File.Exists(string name).

After that, you can just read from the file and see how many bytes were read.

However, why are you reading from 3 different files? Why not just store all usernames and passwords in 1 file in the combination of user:password

That way, you can just open 'users.ext' and read as many lines are you wish. That solution is not only more scalable in case you wish to add more user accounts in the long term, it's probably also easier to maintain the code for that.

In this case, all you need 1 StreamReader: [NOTE: Below is untested C# code. I wrote this from the top of my head without a compiler, and it's been a while since I used StreamReaders, so don't blame me if there are a few compiler errors.]

if (!File.Exists("users.ext"))
return false; /* or something else, dunno how you're doing this atm */

using (StreamReader reader = new StreamReader(new FileStream("users.ext")))
{
do
{
string line = reader.ReadLine();
if (line != null) /* do something with line */
} while (line != null)
}



Also, why are you using char arrays? C# comes with a String class which is way more suitable for the purpose of handling text. And it's a good idea to use those means from the start, because otherwise you'll learn bad habits.

Toolmaker

[Edited by - Toolmaker on May 31, 2009 7:03:30 PM]

Share this post


Link to post
Share on other sites
Thank you

I have adapted my code so that ShowNames() now looks like this:


// ----------------------- SHOW THE NAMES
static void ShowNames()
{
System.IO.StreamReader reader;

if(System.IO.File.Exists("name1.txt"))
{
name1 = System.IO.File.OpenRead("name1.txt");
reader = new System.IO.StreamReader(name1);
System.Console.WriteLine("| 1. ", reader.ReadLine(), "|");
reader.Close();
}
else System.Console.WriteLine("| 1. Empty slot |");

if(System.IO.File.Exists("name2.txt"))
{
name2 = System.IO.File.OpenRead("name2.txt");
reader = new System.IO.StreamReader(name2);
System.Console.WriteLine("| 2. ", reader.ReadLine(), "|" );
reader.Close();
}
else System.Console.WriteLine("| 2. Empty slot |");

if(System.IO.File.Exists("name3.txt"))
{
name3 = System.IO.File.OpenRead("name3.txt");
reader = new System.IO.StreamReader(name3);
System.Console.WriteLine("| 3. ", reader.ReadLine(), "|");
reader.Close();
}
else System.Console.WriteLine("| 3. Empty slot |");
}



Now I can successfully enter a username and password for a slot of my choice.

However:

1. The files all exist, and all contain nothing for some reason, so instead of ever saying 'Empty slot' as I would prefer, it just shows nothing.

2. After saving a username and password, they seem to be reset next time I run the program and don't exist at all.

Share this post


Link to post
Share on other sites
(1) It's because you use the Console.WriteLine function wrong.
Instead of

System.Console.WriteLine("| 1. ", reader.ReadLine(), "|");



it should be

System.Console.WriteLine("| 1. " + reader.ReadLine() + "|");




The comma character does not concatenate strings, it separates arguments in a function call. Use the + operator for string concatenation.


Edit: (2) You forgot to set ischaracter[i] to true when there is an text file for that character. So one if statement in your ShowNames() method should look like:

if (System.IO.File.Exists("name1.txt"))
{
name1 = System.IO.File.OpenRead("name1.txt");
reader = new System.IO.StreamReader(name1);
System.Console.WriteLine("| 1. " + reader.ReadLine() + "|");
ischaracter[1] = true;
reader.Close();
}
else
System.Console.WriteLine("| 1. Empty slot |");

Share this post


Link to post
Share on other sites
It could also be System.Console.WriteLine("| 1. {0}|", reader.ReadLine());. :) The idea is that the first argument to System.Console.WriteLine() is a "format" string, and anything else is a value to "substitute into" the format string. In your original code, you were supplying reader.ReadLine() and "|" as things to substitute in, but didn't specify anywhere to substitute them. The {#} parts of the format string indicate where to insert the other arguments - {0} for the first one after the format string, {1} for the next etc. This is described in detail in the MSDN documentation (see also).

But anyway, the point I wanted to make: repetition is the computer's job, not yours.


// We make a helper function first, which takes the file ID and substitutes it
// in everywhere that it's needed - into the file name itself, and into the
// output text.
static void ShowName(int id) {
String fileName = String.format("name{0}.txt", id);
// We initialize the "name" to a default value, and overwrite that if the
// file is present. That way, there is only one statement in the code
// to do the actual printing.
String result = "Empty slot ";
if (System.IO.File.Exists(fileName)) {
// Use a local variable for the file stream itself. Restrict scope as
// much as possible, in general.
FileStream file = System.IO.File.OpenRead(fileName);
System.IO.StreamReader reader = new System.IO.StreamReader(file);
result = reader.ReadLine();
reader.Close();
}
// Whether or not we read a slot name, we display whatever name we have
// at this point, labelled with the slot ID:
System.Console.WriteLine("| {0}. {1}|", id, result);
}


// ----------------------- SHOW THE NAMES
static void ShowNames() {
// Now all we have to do is loop over the slot IDs.
for (int i = 0; i < 3; ++i) { ShowName(i); }
}



See? Much neater.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this