0

I have a file that looks similar to:

background_color{ 104, 184, 104 }

tile_pattern{
  id = "1",
  ground = "empty",
  default_layer = 2,
  x = 0,
  y = 0,
  width = 8,
  height = 16,
  repeat_mode = "none",
}

tile_pattern{
  id = "999",
  ground = "traversable",
  default_layer = 0,
  x = 40,
  y = 1000,
  width = 8,
  height = 8,
}

tile_pattern{
  id = "abyss_bridge",
  ground = "traversable",
  default_layer = 0,
  x = 280,
  y = 448,
  width = 16,
  height = 16,
}

tile_pattern{
  id = "acid",
  ground = "prickles",
  default_layer = 0,
  x = { 712, 728, 744 },
  y = { 384, 384, 384 },
  width = 16,
  height = 16,
}

tile_pattern{
  id = "lava",
  ground = "lava",
  default_layer = 0,
  x = { 696, 696, 696, 696 },
  y = { 368, 384, 400, 384 },
  width = 16,
  height = 16,
}

tile_pattern{
  id = "hole_parallax.1-1",
  ground = "hole",
  default_layer = 0,
  x = 312,
  y = 224,
  width = 16,
  height = 16,
  scrolling = "self",
}

tile_pattern{
  id = "air_duct.1",
  ground = "wall",
  default_layer = 0,
  x = 880,
  y = 48,
  width = 32,
  height = 24,
  repeat_mode = "none",
}

border_set{
  id = "wall_hole",
  inner = true,
  pattern_0 = "wall_hole.4-1",
  pattern_1 = "wall_hole.1-1",
  pattern_2 = "wall_hole.3-1",
  pattern_3 = "wall_hole.2-1",
  pattern_4 = "wall_hole.corner.2-1",
  pattern_5 = "wall_hole.corner.1-1",
  pattern_6 = "wall_hole.corner.3-1",
  pattern_7 = "wall_hole.corner.4-1",
  pattern_8 = "wall_hole.corner_reverse.2-1",
  pattern_9 = "wall_hole.corner_reverse.1-1",
  pattern_10 = "wall_hole.corner_reverse.3-1",
  pattern_11 = "wall_hole.corner_reverse.4-1",
}

border_set{
  id = "wall_low-1",
  pattern_0 = "wall_low.4-1",
  pattern_1 = "wall_low.1-1",
  pattern_2 = "wall_low.3-1",
  pattern_3 = "wall_low.2-1",
  pattern_4 = "wall_low.corner.2-1",
  pattern_5 = "wall_low.corner.1-1",
  pattern_6 = "wall_low.corner.3-1",
  pattern_7 = "wall_low.corner.4-1",
  pattern_8 = "wall_low.corner_reverse.2-1b",
  pattern_9 = "wall_low.corner_reverse.1-1b",
  pattern_10 = "wall_low.corner_reverse.3-1b",
  pattern_11 = "wall_low.corner_reverse.4-1b",
}

I have it loaded into a string called datString. I am trying to match all of the tile_pattern parts in a single regex expression.

This is what I have so far:

//tilePatterns = new List<TilePatternData>();
        //tile_pattern{
        //id = "10", (string)
        //ground = "empty", (string)
        //default_layer = 2, (number)
        //(x = 72,) (x = { 712, 728, 744 },) (number or table)(only matching the number, have to add in the table)
        //(y = 0,) (y = { 384, 384, 384 },) (number or table)(only matching the number, have to add in the table)
        //width = 8,
        //height = 16,
        //frame_delay(number, optional)(None in test file)
        //mirror_loop (boolean, optional)(None in test file)
        //scrolling (string, optional)(The Ones in the test file do not have a repeat_mode
        //repeat_mode = "none", (string, optional)
        //}
        parts = new Regex("tile_pattern\\s*{\\s*(id\\s*=\\s*\".+\",\\s*ground\\s*=\\s*\".+\",\\s*default_layer\\s*=\\s*-*\\d+,\\s*x\\s*=\\s*\\d+,\\s*y\\s*=\\s*\\d+,\\s*width\\s*=\\s*\\d+,\\s*height\\s*=\\s*\\d+,\\s*\\w*\\s*=*\\s*\"*\\w*\"*),*\\s*}");
        match = parts.Match(datFileString);
        Debug.Log("Match " + match.Success + " : " + match.Length);
        while (match.Success)
        {
            // To easily see which ones are getting loaded in
            Debug.Log(match.Groups[1]);

            //TilePatternData tilePatternData = new TilePatternData();
            //tilePatternData.LoadFromDatFile(match.Groups[1].ToString());
            //tilePatterns.Add(tilePatternData);

            match = match.NextMatch();
        }

which returns a match length of 138 (should be 4830) at a glance through my debug log the only ones that seem to be missing are the 32 patterns that have x = { 712, 728, 744 }, I have not figured out how to add this into my capture group yet any help would be appreciated. Also how would I add the 3 optional fields to make sure if the are all there they would get matched?

Need to add to pattern

tile_pattern{
  id = "acid",
  ground = "prickles",
  default_layer = 0,
  x = { 712, 728, 744 },
  y = { 384, 384, 384 },
  width = 16,
  height = 16,
}
7
  • 1
    Why??? Just throw some more commas in there and you have a json. (It's actually a little bit more. But seriously, it's darn close.) Commented Feb 18, 2021 at 15:41
  • I can imagine \".+\" is the problem here - the .+ is probably eating double quotes, too. Perhaps you could try \"[^\"]+\" instead. Commented Feb 18, 2021 at 15:42
  • 1
    I agree Fildor. But even if you can´t go for JSON I´d strongly discourage you from using a single regex for an entire file. Commented Feb 18, 2021 at 15:42
  • 3
    You should really find out what file format that is and then use a real parser. Does the file have an extension? Commented Feb 18, 2021 at 15:43
  • 3
    It seems to be a Tileset definition file from the Solarus quests game. The remarks section says: This syntax of the tileset data file is actually valid Lua. The engine and the editor internally use Lua to parse it. Commented Feb 18, 2021 at 16:13

3 Answers 3

3

It appears your file input is from a Solarus Quest Tileset data file. According to the specs:

This syntax of the tileset data file is actually valid Lua.

Rather than using Regex, consider using a Lua parser. NLua seems to be able to do a lot with Lua, although it may be a bit much for just parsing variable list notation.

See also: Easiest way to parse a Lua datastructure in C# / .Net

Sign up to request clarification or add additional context in comments.

2 Comments

I know there are Lua parsers out there that you can use. Was just trying to create my own parser along the way and learn some new about Regex expressions.
@JamesLafritz: Yup, nothing wrong with wanting to learn. If you want to write your own parser, may I recommend Sprache? It's much better suited for parsing text like this than Regex is, and it's super fun to learn.
0

Your regex is really much more complex than it has to be... now only you and god know what it does - after a week only god will ;-)

Try this regex: tile_pattern\{(?:[^{}]|(?R))+\}

Please also make sure to set the correct flags (because I don't know the C# syntax for that)

  • global (do not return after the first match)
  • extended (ignore whitespaces)

Also refer to this sample https://regex101.com/r/zqcOBY/1

This also takes care of filtering future new elements in your file without the explicit exclude rules you define in your regex.

1 Comment

This works except for the case of tile_pattern{ id = "acid", ground = "prickles", default_layer = 0, x = { 712, 728, 744 }, y = { 384, 384, 384 }, width = 16, height = 16, }
0

I went with a different solution

string[] patternString = Regex.Split(datFileString, "tile_pattern{");
            string[] borderString = Regex.Split(patternString[patternString.Length - 1], "border_set{");
            patternString[patternString.Length - 1] =
                patternString[patternString.Length - 1]
                    .Remove(patternString[patternString.Length - 1].IndexOf("border_set{", StringComparison.Ordinal));
            tilePatterns = new List<TilePatternData>();

            foreach (string s in patternString)
            {
                if (s.Contains("background_color")) continue;

                TilePatternData tilePatternData = new TilePatternData();
                tilePatternData.LoadFromDatFile(s);
                tilePatterns.Add(tilePatternData);
            }

Then in TilePatternData class

public void LoadFromDatFile(string datFileString)
        {
            //tile_pattern{
            //id = "10", (string)
            //ground = "empty", (string)
            //default_layer = 2, (number)
            //(x = 72,) (x = { 712, 728, 744 },) (number or table)(only matching the number, have to add in the table)
            //(y = 0,) (y = { 384, 384, 384 },) (number or table)(only matching the number, have to add in the table)
            //width = 8,
            //height = 16,
            //frame_delay = 250 (number, optional)
            //mirror_loop = true (boolean, optional)
            //scrolling (string, optional)(The Ones in the test file do not have a repeat_mode
            //repeat_mode = "none", (string, optional)
            //}

            Regex parts = new Regex("\\s*(\\w+)\\s*=\\s*\"(\\w+)\"");
            Match match = parts.Match(datFileString);
            //Debug.Log(match.Success + " " + datFileString);
            while (match.Success)
            {
                string pram = match.Groups[1].ToString();
                string value = match.Groups[2].ToString();
                // Debug.Log(pram + "=" + value);
                switch (pram)
                {
                    case "id":
                        id = value;
                        break;
                    case "ground":
                        Enum.TryParse(value, out ground);
                        break;
                    case "scrolling":
                        Debug.Log(pram + "=" + value);
                        Enum.TryParse(value, out scrollingEffect);
                        break;
                    case "repeat_mode":
                        Enum.TryParse(value, out repeatMode);
                        break;
                }

                match = match.NextMatch();
            }

            parts = new Regex("\\s*(\\w+)\\s*=\\s*(-*\\d+)");
            match = parts.Match(datFileString);
            // Debug.Log(match.Success + ": \\s*(\\w+)\\s*=\\s*(-*\\d+)");
            while (match.Success)
            {
                string pram = match.Groups[1].ToString();
                string value = match.Groups[2].ToString();
                // Debug.Log(pram + "=" + value);
                switch (pram)
                {
                    case "x":
                        int.TryParse(value, out startX);
                        break;
                    case "y":
                        int.TryParse(value, out startY);
                        break;
                    case "width":
                        int.TryParse(value, out width);
                        break;
                    case "height":
                        int.TryParse(value, out height);
                        break;
                    case "default_layer":
                        int.TryParse(value, out defaultLayer);
                        break;
                    case "frame_delay":
                        int.TryParse(value, out height);
                        break;
                }

                match = match.NextMatch();
            }

            // x = { 712, 728, 744 },
            parts = new Regex("\\s*([xy])\\s*=\\s*{(\\s*\\d+,*)*\\s*}");
            match = parts.Match(datFileString);
            Debug.Log(match.Success + ": \\s*([xy])\\s*=\\s*{(\\s*\\d+,*)*\\s*}");
            while (match.Success)
            {
                Debug.Log(match.Groups[2].Captures.Count);
                string pram = match.Groups[1].ToString();
                string[] value = new string[match.Groups[2].Captures.Count];
                // work in progress to extract the table values
            }

            parts = new Regex("\\s*mirror_loop\\s*=\\s*(\\w+)");
            match = parts.Match(datFileString);
            //Debug.Log(match.Success + ": \\s*mirror_loop\\s*=\\s*(\\w+)");
            if (!match.Success) return;
            bool.TryParse(match.Groups[1].ToString(), out mirrorLoop);
            Debug.Log(mirrorLoop);
        }

There may still be a way to simplify this even further.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.