none
How do I put sections of txt file to seperate varibles? RRS feed

  • Question

  • So I have a txt file with several sections and I want to put those sections into diffrent variables.

    For exapmple here is my text file:

    Section1:
    item1

    Section2:
    item2
    item3
    item4

    so I want to put all item from section1 to a varibale and to put all items from section2 to its own variable


    Wednesday, February 20, 2019 3:44 PM

Answers

All replies

  • One of the possible solutions for specific circumstances:

    string[] section1 =
        File
            .ReadLines( @"C:\MyFile.txt" )
            .SkipWhile( s => s.Trim() != "Section1:" )
            .Skip( 1 )
            .TakeWhile( s => !s.TrimEnd().EndsWith( ":" ) )
            .Where( s => !string.IsNullOrWhiteSpace( s ) )
            .ToArray();

    Use a similar code for the second section, or introduce a dictionary for a dynamic number of sections.


    • Edited by Viorel_MVP Wednesday, February 20, 2019 6:12 PM
    Wednesday, February 20, 2019 6:12 PM
  • Looks like a variant of an INI file so I'd use the same approach. Note that the only indication you seem to have of a "section" is the colon at the end of the line so you'll have to use that as an indicator. But that will fail if items can have colons as well. Just be careful of the format, or use a standard format like JSON or INI.

    Here's an example of how you could do it. This is a little more code than the quick solution but it set up to be a little more reasonable as your requirements change.

    class MyFileReader
    {
        public IEnumerable<Section> ReadSections ( string filePath )
        {
            Section current = null;
    
            foreach (var line in File.ReadAllLines(filePath))
            {
                //If this is a new section
                var item = NormalizeLine(line);
                if (IsSection(item, out var name))
                {
                    //Return the previous section, if any
                    if (current != null)
                        yield return current;
    
                    current = new Section() { Name = name };
                } else if (current != null && !String.IsNullOrEmpty(item))
                {
                    //Add to the current section
                    current.Items.Add(item);
                };
            };
    
            //Return the last section, if any
            if (current != null)
                yield return current;
        }
    
        private static string NormalizeLine ( string line )
        {
            return (line ?? "").Trim();
        }
    
        private static bool IsSection ( string value, out string name )
        {
            if (value.EndsWith(":"))
            {
                name = value.TrimEnd(':');
                return true;
            }
    
            name = null;
            return false;            
        }
    }
    
    class Section
    {
        public string Name { get; set; }
    
        public List<string> Items { get; } = new List<string>();
    }

    And the usage.

    class Program
    {
        static void Main ( string[] args )
        {
            var reader = new MyFileReader();
            foreach (var section in reader.ReadSections("test.txt"))
            {
                Console.WriteLine("[{0}]", section.Name);
                foreach (var item in section.Items)
                    Console.WriteLine("\t{0}", item);                    
            };
        }
    }


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, February 20, 2019 6:30 PM
    Moderator
  • Simple!

    Use LINQ to create a query, which will create a Dictionary<string,List<string>>.

    The query and execution:

    string key="";
    var result=File.ReadLines()
        .Aggregate(new Dictionary<string,List<string>>(),(a,s)=>{
            var i=s.IndexOf(':');
            if(i<0)a[key].Add(s);else a[(key=s.Substring(0,i))]=new List<string>();
            return a;
        });

    The execution of the result :

    foreach(var kvp in result)
    {
        Console.WriteLine("\t{0}:",kvp.Key); // section name
        kvp.Value.ForEach(Console.WriteLine); // items
    }

    The outcome:

    /* E.g.,

    Section1: Item11 Item12 Item13 Section2: Item21 Item22 Item23 Item24 Item25 Section3: Item31 Item32 Item33 Item34 */



    Thursday, February 21, 2019 3:16 AM
  • Notice how small the query (in code above) is!

    ( compare it with other codes... )

    Thursday, February 21, 2019 3:20 AM
  • Looks like a variant of an INI file so I'd use the same approach. Note that the only indication you seem to have of a "section" is the colon at the end of the line so you'll have to use that as an indicator. But that will fail if items can have colons as well. Just be careful of the format, or use a standard format like JSON or INI.

    Here's an example of how you could do it. This is a little more code than the quick solution but it set up to be a little more reasonable as your requirements change.

    class MyFileReader
    {
        public IEnumerable<Section> ReadSections ( string filePath )
        {
            Section current = null;
    
            foreach (var line in File.ReadAllLines(filePath))
            {
                //If this is a new section
                var item = NormalizeLine(line);
                if (IsSection(item, out var name))
                {
                    //Return the previous section, if any
                    if (current != null)
                        yield return current;
    
                    current = new Section() { Name = name };
                } else if (current != null && !String.IsNullOrEmpty(item))
                {
                    //Add to the current section
                    current.Items.Add(item);
                };
            };
    
            //Return the last section, if any
            if (current != null)
                yield return current;
        }
    
        private static string NormalizeLine ( string line )
        {
            return (line ?? "").Trim();
        }
    
        private static bool IsSection ( string value, out string name )
        {
            if (value.EndsWith(":"))
            {
                name = value.TrimEnd(':');
                return true;
            }
    
            name = null;
            return false;            
        }
    }
    
    class Section
    {
        public string Name { get; set; }
    
        public List<string> Items { get; } = new List<string>();
    }

    And the usage.

    class Program
    {
        static void Main ( string[] args )
        {
            var reader = new MyFileReader();
            foreach (var section in reader.ReadSections("test.txt"))
            {
                Console.WriteLine("[{0}]", section.Name);
                foreach (var item in section.Items)
                    Console.WriteLine("\t{0}", item);                    
            };
        }
    }


    Michael Taylor http://www.michaeltaylorp3.net

    Ex-mvp Michael (and still mvp Karen),

    Your code is good for an inexperienced newbie, hence, it deserves some fixes and comments:

    FIRST: 

    You created the method ReadSections to enumerate values, but, at the same time, you are using inside it, the non-enumerating method File.ReadAllValues, which is a contradiction, a faux pas.

    SECOND: 

    At the end of the method mentioned above, you wrote:

    //Return the last section, if any

     You should notice that the logic of your program implies that a last section will always exist (unless the file is empty of data, which is out of question).

    THIRD: 

    You implemented a method named NormalizeLine with the parameter string line

    Why do you test line for null value?

    This method above, is private, and inside the context it is valid, the variable line will never ever be null.

    FOURTH: 

    Inside the method ReadSections you use this construct:

    ... && !String.IsNullOrEmpty(item))

    which is naive.

    The NormalizeLine method should also, validate the data ( it's non-sense to parse data to normalize it, without validating it too ).

    FIFTH: 

    You created TWO custom types to solve this simple problem.

    BTW, is this type named Section, also known as repository? (ROFL)

    This type must be defined/valid in any context your code is used; you should pursue the use of anonymous types, always (unless it's impossible).

    SIXTH: 

    When you implemented the type Section, I guess you used the technique known as Jack-The-Stripper, because when instantiating a new object ( of type Section ), part of it is initialized when calling its default ctor, and part of it is initialized by assignment!

    Notice, there's no reason to do it this way.

    You should initialize them by assignment in a single statement, as below:

    current = new Section{ Name = name, Items = new List<string>() };

    and not

    current = new Section() { Name = name };

     as you did.

    Thursday, February 21, 2019 9:18 PM
  • Hi 

    Is your problem solved? If so, please post "Mark as answer" to the appropriate answer, so that it will help other members to find a solution quickly if they face a similar issue.

    Best Regards,

    Jack



    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Monday, February 25, 2019 8:37 AM
    Moderator
  • Oh I'm sorry at the end I just used regex to do this but appreciate everyone's answer
    • Marked as answer by BataBo Jokviu Monday, February 25, 2019 5:27 PM
    Monday, February 25, 2019 5:27 PM
  • Oh I'm sorry at the end I just used regex to do this but appreciate everyone's answer

    Nobody believes in you, while you do not post your solution.

    Please, post your solution, and promote up this forum.

    Or else, make this forum even more rubbish# forum; in this case, the boss of all these moderators should ban your account, or ban mine, for consistence.

    Tuesday, February 26, 2019 1:52 AM
  • I added end to the end of the section:

    section1:

    item1

    item2

    end

    and with regex like this made it into varible:

    var file = File.ReadLines("[REDACTED]");
    Regex regex = new Regex("Section1:((.|\n)*)end");
    var section1 = regex.Match(file.ToString()).Group[1].ToString();


    Wednesday, February 27, 2019 7:05 PM
  • [...]

    Regex regex = new Regex("Section1:((.|\n)*)end");
    

    If you are interested, seems that you should consider at least this fix: "Section1:((.|\n)*?)end".

    Wednesday, February 27, 2019 7:29 PM
  • I added end to the end of the section:

    section1:

    item1

    item2

    end

    and with <g class="gr_ gr_85 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-ins replaceWithoutSep" data-gr-id="85" id="85">regex</g> like this made it into <g class="gr_ gr_125 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace" data-gr-id="125" id="125">varible</g>:

    var file = File.ReadLines("[REDACTED]");
    Regex regex = new Regex("Section1:((.|\n)*)end");
    var section1 = regex.Match(file.ToString()).Group[1].ToString();

    Your text is full of extraneous characters, that are only there, because you don't care about quality, when posting. You are totally sloppy, which makes your text fail to pass the smell test.

    Also, the code you posted is  not  answer to your OP, and it too, does not pass the smell test.

    ADDED:

    I do not understand why any moderator has not yet, unmarked your post as correct answer, because it does not contain any code, and the code you posted later, does not pass the smell test.

    • Edited by ritehere44 Sunday, March 3, 2019 10:59 PM added complement
    Sunday, March 3, 2019 10:48 PM