0

I have a text file which holds input data for a guest at a hotel (Name, Nights staying, Corporate guest?). In the text file it will be displayed as

Ron,5,0
David,2
Ben,4,0

The format is Name,Number of Nights, Corporate Guest. The 0 you can see in some lines indicates they are a corporate guest. If there's no 0 they are not a corporate guest.

This is the code I use to create the file and write to it:

// Creates file to open and to write to
StreamWriter X = new StreamWriter("Guests.txt");
// File will be written to : File Handling\bin\debug

for (int i = 0; i < 8; i++)
{
    WriteLine();
    Write("What is Guests name? : ");
    name = ReadLine();
    Write("How many nights are they staying? : ");
    NightsStaying = int.Parse(ReadLine());
    Write("Corporate Guest? (Y/N) : ");
    CorporateGuest = Convert.ToChar(ReadLine());

    if (CorporateGuest == 'Y')
    {
        X.WriteLine($"{name},{NightsStaying},{accBalance}");
    }
    else
    {
        X.WriteLine($"{name},{NightsStaying}");
    }
}
X.Close();

Next, I created an object array, like this:

object[] arr = new object[10];

My problem now is I need to read in all this data and store in the array, so it can later be displayed on console. I also need to be able to know which records are regular Guests, and which are Corporate Guests.

Any help would be appreciated

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 4
    Beginners have a strange reluctance to create classes. [Don't be like that](https://stackoverflow.com/a/56590345/22437). – Dour High Arch Jul 25 '19 at 17:31
  • @DourHighArch This has all been done in the same class though? – Rhys Hudson Jul 25 '19 at 17:35
  • @RhysHudson You can have more than one class. Write a class to represent a guest. Give it properties to match the columns in the text file. Call that class `Guest`. Write a method that takes one line of text from the file, parses it, and returns a new `Guest` instance with those values. Store your guests in a `List`, not in an array. It's not common to actually use an array in C#. – 15ee8f99-57ff-4f92-890c-b56153 Jul 25 '19 at 17:35
  • @EdPlunkett This where my problem arises i don't quite understand how to read in the file and then make it decide whether its a corporate guest or not to store it in the list – Rhys Hudson Jul 25 '19 at 17:42
  • @RhysHudson Write that `Guest` class. Not much to it. Write that method to create a `Guest` from a line of text: Split the line, parse the integers with Int32.Parse(), create a new Guest instance with the `new` operator, assign the values, return the new Guest you just instantiated. `File.ReadLines(fileName)` will read the lines from the file. Loop through the lines. Pass each line to your Guest parser method, add the result to the list. – 15ee8f99-57ff-4f92-890c-b56153 Jul 25 '19 at 17:45
  • 1
    Break your task down into small problems. Need to read text from a File? Then the [File class](https://learn.microsoft.com/en-us/dotnet/api/system.io.file?view=netframework-4.8) should be useful. Particularly ReadLines. Then figure out how to convert a line to an object. Then figure out how to store a collection of those objects. Then figure out how to loop over that collection and write the output to the console. Break it down into small tasks, research each task. – mason Jul 25 '19 at 17:45
  • You might want to look into creating an object array that looks like `IPerson[]`, where `IPerson` is an interface that both Guest and CorpGuest can implement. – code4life Jul 25 '19 at 18:14

2 Answers2

2

You're looking for something like this.

As they said in the comments, split the task into smaller tasks, research a lot and go start filling the blanks.

Please mark as answer if it helps.

public class Program
{
    public static void Main(string[] args)
    {
        var guests = new List<Guest>();
        var lines = File.ReadLines("path/to/your/file.txt");

        foreach (var line in lines)
        {
            // TODO: parse line and extract data into variable.
            // You're doing it already...
            var name = "";
            var nightsStaying = 0;
            var isCorporate = false; 

            guests.Add(new Guest(name, nightsStaying, isCorporate));
        }

        var outputLines = guests.Select(guest => GuestFormatter.Format(guest));
        File.WriteAllLines("path/to/your/output-file.txt", outputLines);
    }
}

public class Guest
{
    public Guest(string name, int nightsStaying, bool isCorporate)
    {
        Name = name;
        NightsStaying = nightsStaying;
        IsCorporate = isCorporate;
    }   

    public string Name { get; } 
    public int NightsStaying { get; }
    public bool IsCorporate { get; }
}

public static class GuestFormatter
{
    public static string Format(Guest guest)
    {
        return $"{guest.Name},{guest.NightsStaying},{guest.IsCorporate}";
    }
}
Phillippe Santana
  • 2,906
  • 2
  • 28
  • 29
  • You could have a `GuestParser` class, that would accept a CSV string and return a Guest instance. The point in not having it all under a single method is that you have to split responsibilities in your code. Each module you create should be responsible for a single piece of funcionality of your software (search for SRP - Single Responsibility Principle). – Phillippe Santana Jul 25 '19 at 18:23
0

Like others have suggested, you should definitely use a class to store guest data. Let's name it Guest. The class would look something like this:

public class Guest
{
    public string Name { get; set; }
    public int Nights { get; set; }
    public bool CorporateGuest { get; set; }
}

Note:

I'm not quite clear about your logic with Corporate Guest, and it doesn't look a very sound way of doing things. In your explanation you say that you store a 0 if it's a Corporate Guest, and nothing for a regular guest. In your code, you seem to write the value of accBalance, but if we go by your explanation it must always be a 0. I suggest, as I've done in my Guest class, to declare a bool member variable to represent whether a given guest is corporate or not. This will give you the added advantage of each line in the file having same number of columns.

Next, break down your problem. Use different functions to do different tasks, and have one function do only one thing. Since you need to get the same set of inputs, you should write a method that will take in guest information, and give it to you as a Guest object. Something like the following. Do note that I haven't done much error handling.

private static Guest GetGuestInfo()
{
    Console.Write("Guest Name: ");
    var name = Console.ReadLine();
    Console.Write("Nights Staying: ");
    int.TryParse(Console.ReadLine(), out int nights);
    Console.Write("Corporate Guest? (Y/N): ");
    var corporate = Console.ReadLine();

    var guest = new Guest
    {
        Name = name,
        Nights = nights
    };
    if (corporate == "Y")
        guest.CorporateGuest = true;
    else
        guest.CorporateGuest = false;

    return guest;
}

Then, for writing, use another function. I've changed writing to suit my suggestion, that you'd write the true/false boolean value to the file.

private static void WriteGuestToFile(StreamWriter sw, Guest guest)
{
    if (guest != null)
    {
        sw.WriteLine($"{guest.Name},{guest.Nights},{guest.CorporateGuest}");
    }
}

Now in your main program you can simply call these methods:

static void Main()
{
    var guests = new List<Guest>();
    for (int i = 0; i < 2; i++)
    {
        guests.Add(GetGuestInfo());
    }

    using (StreamWriter sw = new StreamWriter("Guests.txt"))
    {
        foreach (var guest in guests)
        {
            WriteGuestToFile(sw, guest);
        }
    }

    Console.ReadLine();
}

Note that StreamWriter is used with a using, which takes care of closing it and releasing memory for you, which is a recommended way of using any stream objects.

Sach
  • 10,091
  • 8
  • 47
  • 84