Search code examples
c#csvparsingfor-loopopenfiledialog

Error when trying to Parse & Convert text from a .csv file


I'm currently stuck on trying to get the output of the last column in the .csv file to work. I need it to be converted to a currency. I'm not sure if I use (i == 6) for the whole column (if this even is a thing that works) or if I have to do (i == 13 || i == 20 || i == 27) etc. for each value that needs to be converted to currency.

What I'm trying to accomplish:

When the Load Inventory button is clicked, inventory records will be read from a text file using the Open File Dialog control, and the lines read from the file is tokenized into field values, parsed (as needed) and then loaded onto the listbox and the list of class instances. The ListBox (and the class instances list) should contain, in addition to all the field values in the input file, also contain columns for QtyHand and Sales. The String.Format() or PadRight() method can be used to format the listbox contents such that all values in a column are aligned assuming you are using a monospaced font.

The first two lines of the .csv file are:

Id,Item Name,StartingQty,QtyMinRestck,QtySold,QtyRStcked,UnitPrice
85-0521031,Shampoo,35,9,0,0,2.89

Any help on sorting this out is greatly appreciated.

public partial class inventoryForm : Form
{
    OpenFileDialog ipFile = new OpenFileDialog();
    public inventoryForm()
    {
        InitializeComponent();
    }

    private void loadInvDataButton_Click(object sender, EventArgs e)
    {
        inventoryListBox.Items.Clear(); //clear listbox items
        if (ipFile.ShowDialog() == DialogResult.OK) //show dialog box
        {
            string[] file = File.ReadAllLines(ipFile.FileName); //tokenize
            foreach (string line in file) //for each line in the file ...
            {
                string[] inventory = line.Split(','); //tokens contain numbers from the .csv file
                for (int i = 0; i < inventory.Length; i++) //loop goes through each item in the inventory
                {
                    if (i == 6) //items in the csv file that require conversion
                    {
                        double price = double.Parse(inventory[i]); //convert the value to currency, then pad
                        inventory[i] = String.Format("{0:C2}", price).PadRight(20, ' ');
                    }
                    else
                    {
                        inventory[i] = inventory[i].PadRight(20, ' '); //just pad the string
                    }
                }
                string item = string.Join("", inventory);
                inventoryListBox.Items.Add(item);
            }
        }
    }
}

Solution

  • I would suggest making this object oriented, and this looks like Inventory. So the first step is to make an inventory class. In the Inventory class there will be method take a string as the filename to load the inventory (this may need to be modified based on your actual data types and error handling requirements).

        public class Inventory
        {
            public string Id { get; set; }
            public string ItemName { get; set; }
            public int StartingQty { get; set; }
            public int QtyMinRestck { get; set; }
            public int QtySold { get; set; }
            public int QtyRStcked { get; set; }
            public decimal UnitPrice { get; set; }
    
            public Inventory()
            {
    
            }
    
            //this overrides the default .ToString() method to provide
            //columnar output and formats the UnitPrice to currrency
            //this requires the following: using System.Globalization;
            public override string ToString()
            {
                return String.Format("{0}{1}{2}{3}{4}{5}{6}"
                    , Id.PadRight(15, ' ')
                    , ItemName.PadRight(30, ' ')
                    , StartingQty.ToString().PadLeft(10, ' ')
                    , QtyMinRestck.ToString().PadLeft(10, ' ')
                    , QtySold.ToString().PadLeft(10, ' ')
                    , QtyRStcked.ToString().PadLeft(10, ' ')
                    , UnitPrice.ToString("C", CultureInfo.CurrentCulture).PadLeft(10, ' '));
            }
    
            //this loads a collection of inventory objects from a file
            //it would ignore any lines with errors
            public IEnumerable<Inventory> Load(string InventoryFileName)
            {
                var inventories = new List<Inventory>();
    
                using (var sr = new StreamReader(InventoryFileName))
                {
                    sr.ReadLine(); //skip the first line
                    while (!sr.EndOfStream)
                    {
                        try
                        {
                            var fields = sr.ReadLine().Split(',');
    
                            inventories.Add(new Inventory
                            {
                                Id = fields[0]
                                ,
                                ItemName = fields[1]
                                ,
                                StartingQty = Int32.Parse(fields[2])
                                ,
                                QtyMinRestck = Int32.Parse(fields[3])
                                ,
                                QtySold = Int32.Parse(fields[4])
                                ,
                                QtyRStcked = Int32.Parse(fields[5])
                                ,
                                UnitPrice = Decimal.Parse(fields[6])
                            });
                        }
                        catch
                        {
                            //handle error here
                        }
    
    
                    }
                }
    
                return inventories;
            }
        }
    

    Now that we have the class you can easily call it and get your inventory back from the file:

    inventoryListBox.Items.Clear(); //clear listbox items

        if (ipFile.ShowDialog() == DialogResult.OK) //show dialog box
        {
            Inventory inventory = new Inventory();
            var inventories = inventory.Load(ipFile.FileName);
            //sets the datasource of the list box to the collection of inventory
            //by default it calls the ToString() method which which overrode
            //to provide columar output
            inventoryListBox.DataSource = inventories;
        }
    

    This is a very basic implementation, but it should make things easier to maintain in the future. You could also use "inventories" as a datasource for your listbox.