Tibia Cams
Tibia Cams

Reputation: 3

Iterate through lines and divide into parts when string appears

I have run into an issue while iterating through the lines of a file. This file has thousands of lines and they look like this:

# Tibia - graphical Multi-User-Dungeon
# MonsterHomes File

# Race     X     Y  Z Radius Amount Regen.

# ====== 0997,0988,09 ====================
    50 31927 31622  9     50      3    700
# ====== 0997,0989,09 ====================
     7 31931 31657  9     50      2    700
# ====== 0997,0990,09 ====================
     5 31914 31691  9     50      4    600
    50 31915 31687  9     50      2    650
# ====== 0997,0991,09 ====================
    26 31930 31712  9     50      1    700
    45 31925 31719  9     50      3    600
# ====== 0998,0986,10 ====================
    49 31947 31564 10     50      1    900
# ====== 0998,0987,08 ====================
    52 31958 31604  8     50      3    600
# ====== 0998,0987,10 ====================
    49 31947 31586 10     50      2    900
# ====== 0998,0988,08 ====================
    26 31963 31637  8     50      2    500
    30 31961 31619  8     50      3    600
    45 31945 31620  8     50      2    700
# ====== 0998,0988,09 ====================
     5 31944 31618  9     50      1    600
     5 31953 31623  9     50      4    700
     5 31963 31631  9     50      1    600
     7 31950 31624  9     50      1    650
    36 31946 31630  9     50      1    600
# ====== 0998,0989,08 ====================
    26 31954 31677  8     50      1    500
    26 31948 31674  8     50      2    500
    36 31942 31670  8     50      1    600
# ====== 0998,0989,09 ====================
    19 31960 31676  9     50      4    700
# ====== 0998,0990,06 ====================
    31 31950 31708  6     50      3    600
# ====== 0998,0990,08 ====================

What I am trying to do is that, whenever # ====== appears at the beginning of a line, a new object called Spawn will be created. And then I will go through that line and all the lines below, until the next # ====== and repeat. First I want to skip the top 5 lines in the file though.

So let's take the text above as example. So the first "Spawn" object should be focusing on this segment:

# ====== 0997,0988,09 ====================
    50 31927 31622  9     50      3    700

The next one will be:

# ====== 0997,0989,09 ====================
     7 31931 31657  9     50      2    700

And the third one:

# ====== 0997,0990,09 ====================
     5 31914 31691  9     50      4    600
    50 31915 31687  9     50      2    650

And it will continue like that.

I have managed to create a new object when the line begins with # ======, but how do I read the lines below until the next occurence of # ======?

static void Main(string[] args)
{
    // Step 1. Read all lines in monster.db file - skip the first 5 lines
    foreach (string line in File.ReadLines("monster.db").Skip(5))
    {
        if (line.StartsWith("# ======"))
        {
            // BEGIN SPAWN
            Spawn spawn = new Spawn();
            
            // Step 2. Get the sector file name
            string filename = line.Replace(" ", "").Replace("=", "").Replace("#", "").Replace(",", "-") + ".sec";
            spawn.SectorFile = filename;
            
            // Step 3. Read the lines below until the next occurence of "# ======"
            // And add the values to the object
            spawn.xxxxx = the values
        }
    }
}

I don't need help with getting the actual values. I just help to find out how I can divide this text file into smaller pieces. And each object will be created whenever a line starts with # ======. I need to read that line and the lines below, until next occurance.

Please let me know if it's unclear. I try to explain as best as I can.

Image explaining it: https://i.sstatic.net/Lu1u4.png

Updated code:

// List of spawns
        public static List<Spawn> Spawns = new List<Spawn> { };
        public static Spawn spawn = null;

        // List of already used coordinates
        public static List<Position> Positions = new List<Position> { };
        public static Random rnd = new Random();

        static void Main(string[] args)
        {
            // Step 1. Read all lines in monster.db file - skip the first 5 lines
            foreach (string line in File.ReadLines("monster.db").Skip(5))
            {
                if (line.StartsWith("# ======"))
                {
                    // BEGIN SPAWN
                    spawn = new Spawn();

                    // Step 2. Get the sector file name
                    string filename = line.Replace(" ", "").Replace("=", "").Replace("#", "").Replace(",", "-") + ".sec";
                    spawn.SectorFile = filename;
                    Spawns.Add(spawn);
                }

                // Continue to the next segment if no more monster lines
                if (spawn == null)
                    continue;

                // Step 3. Get the monsters
                if (!line.Contains("#") && line.Length >= 10)
                {
                    string replaceSpace = line.Replace(" ", "-");
                    string removeDuplicateDash = Regex.Replace(replaceSpace, @"\-+", "-");
                    string dashed = removeDuplicateDash.Substring(1);
                    spawn.MonsterId = Int32.Parse(dashed.Split('-').FirstOrDefault());
                    spawn.SpawnX = Int32.Parse(dashed.Split('-').Skip(1).FirstOrDefault());
                    spawn.SpawnY = Int32.Parse(dashed.Split('-').Skip(2).FirstOrDefault());
                    spawn.SpawnZ = Int32.Parse(dashed.Split('-').Skip(3).FirstOrDefault());
                    spawn.Radius = Int32.Parse(dashed.Split('-').Skip(4).FirstOrDefault());
                    spawn.Amount = Int32.Parse(dashed.Split('-').Skip(5).FirstOrDefault());
                    spawn.Regen = Int32.Parse(dashed.Split('-').Skip(6).FirstOrDefault());
                }
            }

            // List all the spawns
            foreach (Spawn s in Spawns)
            {
                Console.WriteLine($"{s.SectorFile} - {s.MonsterId}, [{s.SpawnX}, {s.SpawnY}, {s.SpawnZ}] - Amount: {s.Amount} - Radius: {s.Radius}");
            }
        }

Upvotes: 0

Views: 103

Answers (3)

Legacy Code
Legacy Code

Reputation: 750

Now I that I really understand what the OP wants :) here is it


            string spawnHeader = null;

            foreach (string line in File.ReadLines("monster.db").Skip(5))
            {
                if (line.StartsWith("# ======"))
                {
                    string filename = line.Replace(" ", "").Replace("=", "").Replace("#", "").Replace(",", "-") + ".sec";
                    spawnHeader = filename;
                }

                if (spawnHeader == null)
                    continue;

                if (!line.Contains("#") && line.Length >= 10)
                {
                    var spawn = new Spawn();
                    spawn.MonsterId = ....
                        ...
                 
                    spawn.SectorFile = spawnHeader;

                    Spawns.Add(spawn);
                }
            }

Upvotes: 1

Idle_Mind
Idle_Mind

Reputation: 39142

Just track the "current" sector filename in a variable outside the loop and use that:

List<Spawn> Spawns = new List<Spawn>();

string curSectorFile = "";
// Step 1. Read all lines in monster.db file - skip the first 5 lines
foreach (string line in File.ReadLines("monster.db").Skip(5))
{
    if (line.StartsWith("# ======"))
    {                    
        // Step 2. Get the sector file name
        string filename = line.Replace(" ", "").Replace("=", "").Replace("#", "").Replace(",", "-") + ".sec";
        curSectorFile = filename;
    }
    else if(curSectorFile != "")
    {
        Spawn sp = new Spawn();
        sp.SectorFile = curSectorFile;
        
        // parse "line"...
        sp.xxx = yyy;
        sp.xxx = yyy;
        sp.xxx = yyy;
        sp.xxx = yyy;

        // add it to some kind of List<>?
        Spawns.Add(sp);
    }
}

Upvotes: 0

jdweng
jdweng

Reputation: 34421

Try following. Code been fully tested. No need to skip lines. Just you the # at beginning of line to indicate where data starts. :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleApplication4
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.txt";
        static void Main(string[] args)
        {
            List<Monster> monsters = new List<Monster>();
            Monster monster = null;

            StreamReader reader = new StreamReader(FILENAME);

            string line = "";
            string[] splitLine;
            while ((line = reader.ReadLine()) != null)
            {
                line = line.Trim();
                if (line.Length > 0)
                {
                    if (line.StartsWith("#"))
                    {
                        if (line.StartsWith("# ======"))
                        {
                            splitLine = line.Split(new char[] { '#', ' ', '=' }, StringSplitOptions.RemoveEmptyEntries);
                            string id = splitLine[0];
                            monster = new Monster();
                            monster.id = id;
                            monsters.Add(monster);
                        }
                    }
                    else
                    {
                        splitLine = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        if (monster.homes == null) monster.homes = new List<Home>();
                        Home home = new Home()
                        {
                            Race = int.Parse(splitLine[0]),
                            X = int.Parse(splitLine[1]),
                            Y = int.Parse(splitLine[2]),
                            Z = int.Parse(splitLine[3]),
                            Radius = int.Parse(splitLine[4]),
                            Amount = int.Parse(splitLine[5]),
                            Regen = int.Parse(splitLine[6])
                        };
                        monster.homes.Add(home);
                    }
                }
            }
        }
    }
    public class Monster
    {
        public string id { get; set; }
        public List<Home> homes { get; set; }
    }
    public class Home
    {
        public int Race { get; set; }
        public int X { get; set; }
        public int Y { get; set; }
        public int Z { get; set; }
        public int Radius { get; set; }
        public int Amount { get; set; }
        public int Regen { get; set; }
    }

}

Upvotes: 0

Related Questions