1

I am running the following C# code to read only the first 10 lines of a .txt file:

using MaterialDesignThemes.Wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Data;

namespace TestEnvironment
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

       
        private void BrowseButton_Click(object sender, RoutedEventArgs e)
        {   

            // Create OpenFileDialog
            Microsoft.Win32.OpenFileDialog openFileDlg = new Microsoft.Win32.OpenFileDialog();

            // Launch OpenFileDialog by calling ShowDialog method
            Nullable<bool> result = openFileDlg.ShowDialog();
            // Get the selected file name and display in a TextBox.
            // Load content of file in a TextBlock
            if (result == true)
            {
                FileNameTextBox.Text = openFileDlg.FileName;
                TextBlock1.Text = "Created on:  " + System.IO.File.GetCreationTime(openFileDlg.FileName).ToString();
                TextBlock1.Text += Environment.NewLine + Environment.NewLine + string.Join("\r\n", System.IO.File.ReadLines(openFileDlg.FileName).Take(10));
                //System.IO.File.ReadLines(openFileDlg.FileName).Take(10).ToString()
                Debug.WriteLine("Txt file contents!");
            }

            // Set filter for file extension and default file extension  
            openFileDlg.DefaultExt = ".txt";
            openFileDlg.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";

            Debug.WriteLine("Txt imported");

            // Set initial directory    
            openFileDlg.InitialDirectory = @"C:\Documents\";

            // Multiple selection with all file types    
            openFileDlg.Multiselect = true;

            Debug.WriteLine("End!");
        }

        private void TabablzControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }
    }
}

Current output

enter image description here

But this what I want. I actually want to print the content in a table format, with column names and rows.

To do so I have searched for a similar question and found this. However, the answer posted there is using a fixed number of columns. But I want to make this method take a dynamic number of columns because my txt files don't have the same length of columns.

The code I run now:

using MaterialDesignThemes.Wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Data;

namespace TestEnvironment
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public DataTable ConvertToDataTable(string filePath, int numberOfColumns)
        {
            DataTable tbl = new DataTable();

            for (int col = 0; col < numberOfColumns; col++)
                tbl.Columns.Add(new DataColumn("Column" + (col + 1).ToString()));

            string[] lines = System.IO.File.ReadAllLines(filePath);

            var lines1 = lines.Take(10);

            Debug.WriteLine(lines1);

            foreach (string line in lines1)
            {
                var cols = line.Split('\t');

                DataRow dr = tbl.NewRow();
                for (int cIndex = 0; cIndex < line.Length; cIndex++)
                {
                    dr[cIndex] = cols[cIndex];
                }

                tbl.Rows.Add(dr);
            }

            return tbl;
        }

        private void BrowseButton_Click(object sender, RoutedEventArgs e)
        {   

            // Create OpenFileDialog
            Microsoft.Win32.OpenFileDialog openFileDlg = new Microsoft.Win32.OpenFileDialog();

            // Launch OpenFileDialog by calling ShowDialog method
            Nullable<bool> result = openFileDlg.ShowDialog();
            // Get the selected file name and display in a TextBox.
            // Load content of file in a TextBlock
            if (result == true)
            {
                FileNameTextBox.Text = openFileDlg.FileName;
                TextBlock1.Text = "Created on:  " + System.IO.File.GetCreationTime(openFileDlg.FileName).ToString();
                var travelTime = ConvertToDataTable(filePath: openFileDlg.FileName, numberOfColumns: 1);
                TextBlock1.Text += Environment.NewLine + Environment.NewLine + travelTime;
                //TextBlock1.Text += Environment.NewLine + Environment.NewLine + string.Join("\r\n", System.IO.File.ReadLines(openFileDlg.FileName).Take(10));
                //System.IO.File.ReadLines(openFileDlg.FileName).Take(10).ToString()
                Debug.WriteLine("Txt file contents!");
            }

            // Set filter for file extension and default file extension  
            openFileDlg.DefaultExt = ".txt";
            openFileDlg.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";

            Debug.WriteLine("Txt imported");

            // Set initial directory    
            openFileDlg.InitialDirectory = @"C:\Documents\";

            // Multiple selection with all file types    
            openFileDlg.Multiselect = true;

            Debug.WriteLine("End!");
        }

        private void TabablzControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }
    }
}

I get the following error:

enter image description here

Thank you in advance for any comments and help. Happy to provide more info is something was not clear.

A dummy .txt file I created my self can be found here

[UPDATE] based on the comments

After running this: (credits to: gkulshrestha)


   public DataTable ConvertToDataTable(string filePath)
    {
        DataTable tbl = new DataTable();
        string[] lines = System.IO.File.ReadAllLines(filePath);

        var lines1 = lines.Take(10);

        for (int col = 0; col < lines1.First().Split('\t').Length; col++)
            tbl.Columns.Add(new DataColumn("Column" + (col + 1).ToString()));

        foreach (string line in lines1)
        {
            var cols = line.Split('\t');

            DataRow dr = tbl.NewRow();
            for (int cIndex = 0; cIndex < cols.Length; cIndex++)
            {
                dr[cIndex] = cols[cIndex];
            }

            tbl.Rows.Add(dr);
        }

        return tbl;
    }
if (result == true)
            {
                FileNameTextBox.Text = openFileDlg.FileName;
                TextBlock1.Text = "Created on:  " + System.IO.File.GetCreationTime(openFileDlg.FileName).ToString();
                TextBlock1.Text += Environment.NewLine + Environment.NewLine + ConvertToDataTable(filePath: openFileDlg.FileName);
                //TextBlock1.Text += Environment.NewLine + Environment.NewLine + string.Join("\r\n", System.IO.File.ReadLines(openFileDlg.FileName).Take(10));
                //System.IO.File.ReadLines(openFileDlg.FileName).Take(10).ToString()
                Debug.WriteLine("Txt file contents!");
            }

I get nothing in return but only the created on Date which is the first Text of my TextBox

enter image description here

[UPDATE 2: Need for encoding the Row.Add content to UTF-8]

I am trying to encode the content of the Row.Add() into UTF-8. Now its ANSI. Searching online I came across online articles.

So my DataGrid table method looks like:

public DataTable ConvertToDataTable(string filePath)
        {
            DataTable tbl = new DataTable();

            // Take the first 10 lines
            var lines = File.ReadLines(filePath).Take(10);

            // Split each line and create an integer sequence where each value 
            // is the number of the splitted elements
            // then get the max value present in this sequence
            var max = lines.Select(x => x.Split('\t').Length).Max();

            // First line contains headers
            string[] headers = lines.First().Split('\t');

            // Now create the table with the max number of columns present
            for (int col = 0; col < max; col++)
                tbl.Columns.Add(new DataColumn(headers[col]));

            //Use the Rows.Add method that accepts an object array
            foreach (string line in lines.Skip(1))
            {

                //tbl.Rows.Add(line.Split('\t'));

                var utf8 = Encoding.UTF8;
                
                byte[] utfBytes = utf8.GetBytes(tbl.Rows.Add(line.Split('\t')));

                var myReturnedString = utf8.GetString(utfBytes);

                tbl.Rows.Add(myReturnedString);
            }

            foreach (DataRow dr in tbl.Rows)
            {

                Debug.WriteLine(dr["Nationality"]);

                string s = dr["Nationality"].ToString();

                byte[] bytes = Encoding.Default.GetBytes(s);
                dr["Nationality"] = Encoding.UTF8.GetString(bytes);

                Debug.WriteLine(dr["Nationality"]);
            }

            return tbl;
        }

But inside the Row.Add() method I get an error:

Cannot convert from 'System.Data.DataRow' to 'char[]'

NikSp
  • 1,262
  • 2
  • 19
  • 42
  • 2
    You have two big problems I can see. First is you're creating only one column in your datatable. What happens if you have more than one column you've split into cols? Also, you're iterating up to line.Length ( the number of characters you have in a line) rather than cols.Length ( the number of cols you split that line into). – Andy Sep 20 '20 at 15:14
  • @Andy yeah I aknowledge those two issues you mention. Thank you. I am new in C# and I know what the end result want to be but it is hard for me to replicate it in C# code. – NikSp Sep 20 '20 at 15:25
  • 1
    A DataTable doesn't automatically display its contents as a string. When you add the variable name to the output in textbox1 you get only the class name IE: System.Data.DataTable – Steve Sep 20 '20 at 15:36
  • @Steve yeah I thought of it. So I am searching for ways to print the content of a datatable to a textbox – NikSp Sep 20 '20 at 15:38
  • https://www.wpftutorial.net/DataGrid.html – Steve Sep 20 '20 at 15:39
  • 1
    Also consider that between ReadAllLines and ReadLines there is a big difference in performance if the file is large. You need to Take(10) so use ReadLines(filePath).Take(10) https://stackoverflow.com/questions/21969851/what-is-the-difference-between-file-readlines-and-file-readalllines – Steve Sep 20 '20 at 15:43
  • @Steve relatively to your last comment, I totally agree with you. But the command: ```string[] lines =System.IO.File.ReadAllLines(filePath).Take(10);``` is not correct. Returns an error: Error CS0266 Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'string[]'. An explicit conversion exists (are you missing a cast?) – NikSp Sep 20 '20 at 15:51
  • 1
    Add _.ToArray()_ – Steve Sep 20 '20 at 15:53
  • @Steve Thank you! I apologize if I make silly mistakes with C# – NikSp Sep 20 '20 at 15:55
  • Nothing to worry about. Learning is a difficult road full of mistakes. Changed my answer to show you a shorter way to prepare the table – Steve Sep 20 '20 at 17:17

3 Answers3

2

The error message tells you that one of your lines has more columns than the value passed in the numberOfColumns variable. So you build a table with a certain number of columns but then you find a line with more data.

You can simply change the check for the exit condition on the for loop

for (int cIndex = 0; cIndex < cols.Length && cIndex < numberOfColumns; cIndex++)

However this will simply ignore the extra data. You should understand why a line has more data and decide if you want to keep that extra or not.

In case you don't know beforehand the number of columns present in your file, then you can change the ConvertToDataTable and allow it to discover the max number of columns (Always keep in mind that with 10 lines there is no performace problem, but with large files things change)

// No need to pass the number of columns, let the method discover it
public DataTable ConvertToDataTable(string filePath)
{
    DataTable tbl = new DataTable();

    // Take the first 10 lines
    var lines = File.ReadLines(filePath).Take(10);

    // Split each line and create an integer sequence where each value 
    // is the number of the splitted elements
    // then get the max value present in this sequence
    var max = lines.Select(x => x.Split('\t').Length).Max();

    // First line contains headers
    string[] headers = lines.First().Split('\t');

    // Now create the table with the max number of columns present
    for (int col = 0; col < max; col++)
        tbl.Columns.Add(new DataColumn(headers[col]));

    // You can simplify a lot your loop using the Rows.Add method that
    // accepts an object array     
    foreach (string line in lines.Skip(1))
    {
        tbl.Rows.Add(line.Split('\t'));
    }
    return tbl;
}
Steve
  • 213,761
  • 22
  • 232
  • 286
  • Hey Steve, can you edit your code to include the first line as the headers? Because now my headers are like "Column 1", "Column 2", etc. But I would like my first line to be the headers and the rest of the 9 lines to be the content – NikSp Sep 20 '20 at 19:56
  • Done (without testing it). However now the initial purpose to be free from lines with different length has been lost. The first line is used to create the columns, so a next line with more data will get you back to the initial exception – Steve Sep 20 '20 at 20:49
  • Hey @Steve can you please check an update I made for encoding the line to UTF-8 right when I am insertion the Row.Add() to the DataGrid. – NikSp Sep 21 '20 at 12:15
  • 1
    Sorry @NikSp but I think you should post a new question. This one has already drifted alot from the original one and it is not useful for the site to have this kind oh [chamaleon questions](https://meta.stackexchange.com/questions/43478/exit-strategies-for-chameleon-questions#duplicate=0). Posting a new question will attract the attention of new answerers not only mine. In any case you can post a link to your new question here so I will be notified. – Steve Sep 21 '20 at 12:21
  • Ok Steve. Thank you for the note. – NikSp Sep 21 '20 at 12:24
  • Steve I posted the question here https://stackoverflow.com/questions/63993975/encode-to-utf-8-the-content-of-a-row-add-methond-when-binding-datagrid-with-t – NikSp Sep 21 '20 at 14:08
1

Since you do not know before hand the number of columns your text file will have, you cannot pass numberOfColumns to ConvertToDataTable function. Better you determine the column count inside the function itself after file has been read.

Try this:

    public DataTable ConvertToDataTable(string filePath)
    {
        DataTable tbl = new DataTable();
        string[] lines = System.IO.File.ReadAllLines(filePath);

        var lines1 = lines.Take(10);

        for (int col = 0; col < lines1.First().Split('\t').Length; col++)
            tbl.Columns.Add(new DataColumn("Column" + (col + 1).ToString()));

        foreach (string line in lines1)
        {
            var cols = line.Split('\t');

            DataRow dr = tbl.NewRow();
            for (int cIndex = 0; cIndex < cols.Length; cIndex++)
            {
                dr[cIndex] = cols[cIndex];
            }

            tbl.Rows.Add(dr);
        }

        return tbl;
    }
gkulshrestha
  • 855
  • 1
  • 6
  • 11
0

For a such a case you can use

    public DataTable ConvertToDataTable(string filePath)
    {
        DataTable tbl = new DataTable();
        string[] lines = System.IO.File.ReadAllLines(filePath);
        

        // just use lines.Take(10) if you want only 10 lines

        for (int col = 0; col < lines.First().Split('\t').Length; col++)
            tbl.Columns.Add(new DataColumn("Column" + (col + 1).ToString()));

        foreach (string line in lines)
        {
            var cols = line.Split('\t');

            DataRow dr = tbl.NewRow();
            for (int cIndex = 0; cIndex < cols.Length; cIndex++)
            {
                dr[cIndex] = cols[cIndex];
            }

            tbl.Rows.Add(dr);
        }

        return tbl;
    }
Ahmed
  • 85
  • 3
  • 8