【问题标题】:Regex pattern matching with multiple groups C#正则表达式模式匹配多个组 C#
【发布时间】:2021-02-18 15:38:39
【问题描述】:

我有一个类似于以下内容的文件:

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",
}

我已将它加载到一个名为 datString 的字符串中。我正在尝试匹配单个正则表达式中的所有 tile_pattern 部分。

这是我目前所拥有的:

//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();
        }

通过我的调试日志一目了然地返回匹配长度 138(应该是 4830)唯一似乎缺少的是具有 x = { 712, 728, 744 } 的 32 种模式,我还没有想到了解如何将其添加到我的捕获组中,但我们将不胜感激。另外,我将如何添加 3 个可选字段以确保它们是否都匹配?

需要添加到图案中

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

【问题讨论】:

  • 为什么??? 只需在其中添加一些逗号,您就有了一个 json。 (它实际上是一点点。但说真的,它非常接近。)
  • 我可以想象\".+\" 是这里的问题 - .+ 可能也在吃双引号。也许你可以试试\"[^\"]+\"。
  • 我同意菲尔多。但即使您不能使用 JSON,我也强烈建议您不要对整个文件使用单个正则表达式。
  • 您应该真正找出文件格式,然后使用真正的解析器。文件有扩展名吗?
  • 这似乎是 Solarus 任务游戏中的 Tileset definition file。备注部分说: tileset 数据文件的这种语法实际上是有效的 Lua。引擎和编辑器在内部使用 Lua 对其进行解析。

标签: c# regex


【解决方案1】:

您的文件输入似乎来自 Solarus Quest Tileset 数据文件。根据the specs:

tileset 数据文件的这种语法实际上是有效的 Lua。

考虑使用 Lua 解析器,而不是使用正则表达式。 NLua 似乎可以用 Lua 做很多事情,尽管对于解析变量列表表示法来说可能有点多。

另见:Easiest way to parse a Lua datastructure in C# / .Net

【讨论】:

  • 我知道你可以使用 Lua 解析器。一直在尝试创建自己的解析器并学习一些关于正则表达式的新知识。
  • @JamesLafritz:是的,想要学习没有错。如果你想写自己的解析器,我可以推荐Sprache吗?它比 Regex 更适合解析这样的文本,而且学习起来非常有趣。
【解决方案2】:

你的正则表达式确实比它必须的复杂得多......现在只有你和上帝知道它的作用 - 一周后只有上帝会;-)

试试这个正则表达式:tile_pattern\{(?:[^{}]|(?R))+\}

还请确保设置正确的标志(因为我不知道 C# 语法)

  • 全局(第一次匹配后不返回)
  • 扩展(忽略空格)

另请参阅此示例https://regex101.com/r/zqcOBY/1

这还负责过滤文件中未来的新元素,而无需您在正则表达式中定义的明确排除规则。

【讨论】:

  • 除了 tile_pattern{ id = "acid", ground = "prickles", default_layer = 0, x = { 712, 728, 744 }, y = { 384, 384, 384 },宽度 = 16,高度 = 16,}
【解决方案3】:

我选择了不同的解决方案

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);
            }

然后在 TilePatternData 类中

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);
        }

可能还有一种方法可以进一步简化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-08
    • 2012-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    相关资源
    最近更新 更多