How to process a txt file for parsing with a function in C#

Hakan
Hakan used Ask the Experts™
on
Hello,

I've a txt file like below and i'd like to get some informations from it with a function but need a expert help for parsing.

  139 142    :No. nodes, No. modes:
 part                                            
      new modal                                 =                                                                                 
           refmod                                
                mass                            = 2.000D+03
                nelastq                         = 142
                ielastq (   1)                  = Eigen Mode    7 :        3.895 Hz
                ielastq (   2)                  = Eigen Mode    8 :        8.914 Hz
                ielastq (   3)                  = Eigen Mode    9 :       10.455 Hz
                ielastq (   4)                  = Eigen Mode   10 :       10.633 Hz
                ielastq (   5)                  = Eigen Mode   11 :       14.134 Hz
                ielastq (   6)                  = Eigen Mode   12 :       14.230 Hz
                ielastq (   7)                  = Eigen Mode   13 :       14.839 Hz
                ielastq (   8)                  = Eigen Mode   14 :       15.543 Hz
                ielastq (   9)                  = Eigen Mode   15 :       16.810 Hz
                ielastq (  10)                  = Eigen Mode   16 :       16.921 Hz
                ielastq (  11)                  = Eigen Mode   17 :       17.858 Hz
                ielastq (  12)                  = Eigen Mode   18 :       18.822 Hz
                ielastq (  13)                  = Eigen Mode   19 :       19.006 Hz
                ielastq (  14)                  = Eigen Mode   20 :       19.335 Hz
                ielastq (  15)                  = Eigen Mode   21 :       19.430 Hz
                ielastq (  16)                  = IRM           1 :       59.876 Hz
                ielastq (  17)                  = IRM           2 :       60.495 Hz
                ielastq (  18)                  = IRM           3 :       61.735 Hz
                ielastq (  19)                  = IRM           4 :       64.041 Hz
                ielastq (  20)                  = IRM           5 :       67.065 Hz
                ielastq (  21)                  = IRM           6 :       67.663 Hz
                ielastq (  22)                  = IRM           7 :       69.528 Hz
           end refmod   
	   
			Some Text Data
			.
			.
			.     

Open in new window

           

I'd like to get informations from  ielastq (   *id*) rows.

If there's a function like

public static string[] GetIElastiq(int id)
{
// some code here
}

Open in new window


if i wrote id 10 that function i'd like to get result as [ {Eigen Mode}, {16}, {16.921} ]

It'll found the row:          
   ielastq (  10)                  = Eigen Mode   16 :       16.921 Hz

Open in new window


and parse for me.

The values always between "refmod" and "end refmod" so Textreader only can focus this region.

Any help would be very great!!

Thank you.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
ste5anSenior Developer

Commented:
What have you so far?

Just "loop" over the input file / stream by reading line for line. Seems like it is sufficient to look for lines with "ielastq". Parsing can be done here by splitting the line using fixed positions with SubString.

Commented:
Something like this should suffice:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace EE_Q29167461
{
    class Program
    {
        static void Main(string[] args)
        {
            var contents = System.IO.File.ReadAllLines($"{typeof(Program).Namespace}.txt").Where(x => x.IndexOf("ielastq", StringComparison.OrdinalIgnoreCase) > -1);
            var matches = contents.GetIElastiqById(10);
            foreach (var match in matches)
            {
                Console.WriteLine(match);
            }
            Console.ReadLine();
        }
    }

    static class Extensions
    {
        public static IEnumerable<string> GetIElastiqById(this IEnumerable<string> data, int id)
        {
            var parameter = default(int);
            return data.Where(x => 
                Regex.Match(x, @"\(([^)]+)\)").Groups.Count > 1 && 
                int.TryParse(Regex.Match(x, @"\(([^)]+)\)").Groups[1].Value.Trim(), out parameter) && 
                parameter == id
            );
        }
    }
}

Open in new window

Produces the following output -Capture.PNG-saige-

Author

Commented:
it_saige, thank you so much :) It fits very well.
ste5anSenior Developer

Commented:
Reading the entire file is normally slower (lines > 255)..

namespace ConsoleCS
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;

    public class Program
    {
        public static void Main(string[] args)
        {
            IElastq ielastq = new IElastq(@"c:\temp\data.txt");
            var dataPoint = ielastq[10];
            Console.WriteLine(dataPoint);

            Console.WriteLine("\nDone.");
            Console.ReadLine();
        }
    }

    public class IElastq
    {
        private Dictionary<int, Tuple<string, string, double>> iElastq = new Dictionary<int, Tuple<string, string, double>>();

        public Tuple<string, string, double> this[int i]
        {
            get
            {
                return this.iElastq[i];
            }
        }

        public IElastq(string filename)
        {
            using (StreamReader file = new StreamReader(filename))
            {
                string line;
                bool inRefMod = false;
                while ((line = file.ReadLine()) != null)
                {
                    line = line.Trim();
                    if (inRefMod)
                    {
                        if (line.StartsWith("ielastq"))
                        {
                            int number = int.Parse(line.Substring(9, 4));
                            string description = line.Substring(33, 17).Trim();
                            double frequency = double.Parse(line.Substring(51, 14), new CultureInfo("en-US"));
                            string unit = line.Substring(65, line.Length - 65);
                            this.iElastq.Add(number, new Tuple<string, string, double>(description, unit, frequency));
                        }

                        if (line.Equals("end refmod"))
                        {
                            break;
                        }
                    }

                    if (line.Equals("refmod"))
                    {
                        inRefMod = true;
                    }
                }
            }
        }
    }
}

Open in new window


Capture.PNG

Author

Commented:
@ste5an,

Thanks for the reply, it's not guaranteed the locations will be same so substring may not work in some cases. Also is tuple useful? I think it's making more slower the application.

@it_saige
Is'it possible to limit readed section to between "refmod" and "end refmod" ?

Author

Commented:
Also i tried to parse output string but didnt succeded how is it possible to get single results from output string

                ielastq (  10)                  = Eigen Mode   16 :       16.921 Hz

Open in new window

Like below;
				Eigen Mode
				16
				16.921

Open in new window

Senior Developer
Commented:
Thanks for the reply, it's not guaranteed the locations will be same so substring may not work in some cases.
Sure about this? Cause the files of this kind I've worked with had a fixed format. But in the end, this is trivial as you just need to determine the parsing points..

E.g.

    public class IElastq
    {
        private Dictionary<int, Tuple<string, string, double>> iElastq = new Dictionary<int, Tuple<string, string, double>>();

        public Tuple<string, string, double> this[int i]
        {
            get
            {
                return this.iElastq[i];
            }
        }

        public IElastq(string filename)
        {
            using (StreamReader file = new StreamReader(filename))
            {
                string line;
                bool inRefMod = false;
                while ((line = file.ReadLine()) != null)
                {
                    line = line.Trim();
                    if (inRefMod)
                    {
                        if (line.StartsWith("ielastq"))
                        {
                            int equal = line.IndexOf('=');
                            int colon = line.IndexOf(':', equal + 1);
                            int number = int.Parse(line.Substring(9, 4));
                            string description = line.Substring(equal + 1, colon - equal - 1).Trim();
                            line = line.Substring(colon + 1).Trim();
                            int space = line.IndexOf(' ');
                            double frequency = double.Parse(line.Substring(0, space - 1), new CultureInfo("en-US"));
                            string unit = line.Substring(space + 1, line.Length - space - 1).Trim();
                            this.iElastq.Add(number, new Tuple<string, string, double>(description, unit, frequency));
                        }

                        if (line.Equals("end refmod"))
                        {
                            break;
                        }
                    }

                    if (line.Equals("refmod"))
                    {
                        inRefMod = true;
                    }
                }
            }
        }
    }
}

Open in new window

Depending on the complexity, you need to test using regex for parsing the string.

Also is tuple useful? I think it's making more slower the application.
It depends on your concrete use-case, which is not clear. Cause C# is object oriented and the "value" behind ielastq(10) is not a scalar, we should return a struct or class. I've used tuple for demonstration. Normally I would always use its own class:

namespace ConsoleCS
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text.RegularExpressions;

    public class Program
    {
        public static void Main(string[] args)
        {
            System.Diagnostics.Stopwatch stopWatch1 = new System.Diagnostics.Stopwatch();
            System.Diagnostics.Stopwatch stopWatch2 = new System.Diagnostics.Stopwatch();
            stopWatch1.Start();

            for (int i = 0; i < 10; i++)
            {
                Container ielastq = new Container(@"c:\temp\data.txt");
                var dataPoint = ielastq[10];
                Console.WriteLine(dataPoint);
            }

            stopWatch1.Stop();
            stopWatch2.Start();

            for (int i = 0; i < 10; i++)
            {
                var contents = System.IO.File.ReadAllLines(@"c:\temp\data.txt").Where(x => x.IndexOf("ielastq", StringComparison.OrdinalIgnoreCase) > -1);
                var matches = contents.GetIElastiqById(10);
                foreach (var match in matches)
                {
                    var match2 = match;
                    int equal = match2.IndexOf('=');
                    int colon = match2.IndexOf(':', equal + 1);
                    int number = int.Parse(match2.Substring(9, 4));
                    string description = match2.Substring(equal + 1, colon - equal - 1).Trim();
                    match2 = match2.Substring(colon + 1).Trim();
                    int space = match2.IndexOf(' ');
                    double frequency = double.Parse(match2.Substring(0, space - 1), new CultureInfo("en-US"));
                    string unit = match2.Substring(space + 1, match2.Length - space - 1).Trim();
                    var dataPoint = new Tuple<string, string, double>(description, unit, frequency);
                    Console.WriteLine(dataPoint);
                }
            }

            stopWatch2.Stop();
            Console.WriteLine("Time Taken : " + stopWatch1.Elapsed.Hours.ToString() + " : " + stopWatch1.Elapsed.Minutes.ToString() + " : " + stopWatch1.Elapsed.Seconds.ToString() + " : " + stopWatch1.Elapsed.Milliseconds.ToString());
            Console.WriteLine("Time Taken : " + stopWatch2.Elapsed.Hours.ToString() + " : " + stopWatch2.Elapsed.Minutes.ToString() + " : " + stopWatch2.Elapsed.Seconds.ToString() + " : " + stopWatch2.Elapsed.Milliseconds.ToString());

            Console.WriteLine("\nDone.");
            Console.ReadLine();
        }
    }

    static class Extensions
    {
        public static IEnumerable<string> GetIElastiqById(this IEnumerable<string> data, int id)
        {
            var parameter = default(int);
            return data.Where(x =>
                Regex.Match(x, @"\(([^)]+)\)").Groups.Count > 1 &&
                int.TryParse(Regex.Match(x, @"\(([^)]+)\)").Groups[1].Value.Trim(), out parameter) &&
                parameter == id
            );
        }
    }

    public class IElastq
    {
        public IElastq(string description, string frequencyUnit, double frequenceValue, int number)
        {
            this.Description = description;
            this.FrequencyUnit = frequencyUnit;
            this.FrequencyValue = frequenceValue;
            this.Number = number;
        }

        public static IElastq Parse(string line)
        {
            int equal = line.IndexOf('=');
            int colon = line.IndexOf(':', equal + 1);
            int number = int.Parse(line.Substring(9, 4));
            string description = line.Substring(equal + 1, colon - equal - 1).Trim();
            line = line.Substring(colon + 1).Trim();
            int space = line.IndexOf(' ');
            double frequencyValue = double.Parse(line.Substring(0, space - 1), new CultureInfo("en-US"));
            string frequencyUnit = line.Substring(space + 1, line.Length - space - 1).Trim();
            return new IElastq(description, frequencyUnit, frequencyValue, number);
        }

        public string Description { get; private set; }

        public string FrequencyUnit { get; private set; }

        public double FrequencyValue { get; private set; }

        public int Number { get; private set; }

        public override string ToString()
        {
            return string.Format($"Number: {this.Number}; Description: {this.Description}; Frequency Unit: {this.FrequencyUnit}; Frequency value: {this.FrequencyValue}.");
        }
    }


    public class Container
    {
        private List<IElastq> iElastq = new List<IElastq>();

        public IElastq this[int index]
        {
            get
            {
                return this.iElastq.FirstOrDefault(item => item.Number == index);
            }
        }

        public Container(string filename)
        {
            using (StreamReader file = new StreamReader(filename))
            {
                string line;
                bool inRefMod = false;
                while ((line = file.ReadLine()) != null)
                {
                    line = line.Trim();
                    if (inRefMod)
                    {
                        if (line.StartsWith("ielastq"))
                        {
                            IElastq item = IElastq.Parse(line);
                            if (item != null)
                            {
                                this.iElastq.Add(item);
                            }
                        }

                        if (line.Equals("end refmod"))
                        {
                            break;
                        }
                    }

                    if (line.Equals("refmod"))
                    {
                        inRefMod = true;
                    }
                }
            }
        }
    }
}

Open in new window

Commented:
Using some of ste5an's code, here is the simplest implementation I came up with:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace EE_Q29167461
{
    class Program
    {
        static void Main(string[] args)
        {
            var contents = GetIElastqRows($"{typeof(Program).Namespace}.txt");
            var matches = contents.GetIElastqById(10);
            foreach (var match in matches)
            {
                Console.WriteLine(match);
            }
            Console.ReadLine();
        }

        private static List<string> GetIElastqRows(string file)
        {
            var results = new List<string>();
            using (var reader = new StreamReader(file))
            {
                string line = default(string);
                bool inRefMod = default(bool);
                while(!inRefMod)
                {
                    line = reader.ReadLine();
                    if (!string.IsNullOrEmpty(line) && line.IndexOf("refmod", StringComparison.OrdinalIgnoreCase) > -1)
                    {
                        inRefMod = true;
                    }
                }

                while (inRefMod)
                {
                    line = reader.ReadLine();
                    if (!string.IsNullOrEmpty(line))
                    {
                        if (line.IndexOf("end refmod", StringComparison.OrdinalIgnoreCase) > -1)
                        {
                            inRefMod = false;
                        }
                        else if (line.IndexOf("ielastq", StringComparison.OrdinalIgnoreCase) > -1)
                        {
                            results.Add(line);
                        }
                    }
                }
            }
            return results;
        }
    }

    static class Extensions
    {
        public static IEnumerable<IElastq> GetIElastqById(this IEnumerable<string> data, int id)
        {
            var parameter = default(int);
            return data.Where(x => 
                Regex.Match(x, @"\(([^)]+)\)").Groups.Count > 1 && 
                int.TryParse(Regex.Match(x, @"\(([^)]+)\)").Groups[1].Value.Trim(), out parameter) && 
                parameter == id
            ).Select(x => IElastq.Parse(x));
        }
    }

    class IElastq
    {
        private int id;
        private string description;
        private string unit;
        private string value;

        public int Id => id;
        public string Description => description;
        public string Unit => unit;
        public string Value => value;

        public static IElastq Parse(string line)
        {
            var result = new IElastq();
            var columns = line.Split(new[] { '=', ':' }, StringSplitOptions.RemoveEmptyEntries);
            int.TryParse(Regex.Match(columns[0], @"\(([^)]+)\)").Groups[1].Value.Trim(), out result.id);
            result.description = Regex.Match(columns[1], @"\D+").Value.Trim();
            result.unit = Regex.Match(columns[1], @"\d+").Value.Trim();
            result.value = columns[2].Trim();

            return result;
        }

        public override string ToString()
        {
            return $"{{ Id: {Id}, Description: {Description}, Unit: {Unit}, Value: {Value} }}";
        }
    }
}

Open in new window

Which now produces the following output -Capture.PNG-saige-

Author

Commented:
ste5an, it_saige,

Thank you so much this is the ever never best solution for my case. Thanks a lot again.

Author

Commented:
Maybe it's a basic question but i'd like to know is this IElastq objects need to release ? Are they keep in memory when procedure ends? I mean their lifetime.
ste5anSenior Developer

Commented:
In short: .NET manages the life time of objects at runime. When an object is no longer references it is free during the next run of the garbage collector.
For further details and exceptions see Fundamentals of garbage collection.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial