【问题标题】:Parsing tnsnames.ora using regex使用正则表达式解析 tnsnames.ora
【发布时间】:2019-08-10 07:40:32
【问题描述】:

我正在尝试使用正则表达式从我的 tnsnames 文件中提取一些信息。我从以下模式开始:

MYSCHEMA *? = *?[\W\w\S\s]*\(HOST *?= *?(?<host>\w+\s?)\)\s?\(PORT *?= *?(?<port>\d+)\s?\)[\W\w\S\s]*\(SERVICE_NAME *?= *?(?<servicename>\w+)\s?\)

当 MYSCHEMA 是文件中唯一的架构时,它工作得很好,但是当 MYSCHEMA 之后列出了其他架构时,它一直匹配到最后一个架构。

我已经创建了一个新模式:

MYSCHEMA *=\s*\(DESCRIPTION =\s*\(ADDRESS *= *\(PROTOCOL *= *TCP\)\(HOST *= *(?<host>\w+)\)\(PORT *= *(?<port>\d+)\)\)\s*\(CONNECT_DATA *=\s*(?<serverdedicated>\(SERVER *= *DEDICATED\))\s*\(SERVICE_NAME *= *(?<servicename>[\w\.]+) *\)\s*\)\s*\)

此模式仅匹配 MYSCHEMA,但我必须添加 MYSCHEMA 条目中出现的每个元素,如果它不包含所有相同的元素,它将不会匹配 MYOTHERSCHEMA。

理想情况下,我想要一个仅匹配 MYSCHEMA 条目的模式,并捕获 HOST、PORT 和 SERVICE NAME,以及可选的 (SERVER = DEDICATED)(我在第一个模式中没有)到命名组。

以下是我用于测试的示例 tnsname:

SOMESCHEMA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = REMOTEHOST)(PORT = 1234))
    )
    (CONNECT_DATA = (SERVICE_NAME = REMOTE)
    )
  )

MYSCHEMA =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = MYHOST)(PORT = 1234))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = MYSERVICE.LOCAL )
    )
  )

MYOTHERSCHEMA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = MYHOST)(PORT = 1234))
    )
    (CONNECT_DATA = 
      (SERVICE_NAME = MYSERVICE.REMOTE)
    )

  )

SOMEOTHERSCHEMA = 
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = LOCALHOST)(PORT = 1234))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = LOCAL)
    )
  )

【问题讨论】:

  • 正则表达式不适用于平衡括号,请参阅上一条评论中的链接以获得更好的解决方案。
  • @Kobi - 我无法证明将另一个第三方产品添加到我们的开发环境中可以通过其他方式完成。
  • Welton - 那是你的事,但我会使用解析器。发布的解决方案没有太多代码,但是有一个很少有人能理解和支持的正则表达式,我不知道它是否支持full syntax - 它可能会失败一晚......当然,有可能这个正是您所需要的。只是提供一个替代方案。祝你好运!

标签: c# .net regex oracle tnsnames


【解决方案1】:

应该这样做,使用平衡组。并根据您的需要修改开关/外壳。

class TnsRegex
{
    public void Test()
    {
        Regex reTns = new Regex(_pattern, RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
        MatchCollection matchCollection = reTns.Matches(_text);

        foreach (Match match in matchCollection)
        {
            foreach (Capture capture in match.Groups["Settings"].Captures)
            {
                string[] setting = capture.Value.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                string key = setting[0].Trim();
                string val = setting[1].Trim();
                if (val.Contains("(")) continue;
                switch (key)
                {
                    case "HOST":
                        break;
                    case "PORT":
                        break;
                    case "SERVICE_NAME":
                        break;
                    case "SERVER":
                        break;
                }
                Console.WriteLine(key + ":" + val);
            }
        }
    }
    string _pattern = @"
        MYSCHEMA\s+=\s+\(
        [^\(\)]*
        (
                    (
                                (?<Open>\()
                                [^\(\)]*
                    )+
                    (
                                (?<Settings-Open>\))
                                [^\(\)]*
                    )+
        )*
        (?(Open)(?!))
    \)";

    string _text = @"
    MYSCHEMA =
      (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = MYHOST)(PORT = 1234))
        (CONNECT_DATA =
          (SERVER = DEDICATED)
          (SERVICE_NAME = MYSERVICE.LOCAL )
        )
      )

    SOMESCHEMA =
      (DESCRIPTION =
        (ADDRESS_LIST =
          (ADDRESS = (PROTOCOL = TCP)(HOST = REMOTEHOST)(PORT = 1234))
        )
        (CONNECT_DATA = (SERVICE_NAME = REMOTE)
        )
      )
    ";
}

【讨论】:

  • 我在上面尝试了您的解决方案,用 \s* 替换了长字符串,但是当我包含 Settings-Open 组时,它停止查找任何匹配项。
  • 保留文字空格.. 使用 RegexOptions.IgnorePatternWhitespace 选项处理它们。如果要删除它们,只需删除空格,不要替换它。
  • 糟糕,抱歉。我刚刚将正则表达式模式复制到 RegexBuddy 中以对我的示例进行快速测试,并遇到了上述问题。我没有注意到 RegexOptions。
【解决方案2】:

以下正则表达式将解析出单个 TNS 条目,您所要做的就是按照您认为适合名称/值的方式解析每个结果

([\w-]+)\s*=(?:\s|.)+?\)\s*\)\s*\)\s*(?=[\w\-])

示例:https://regexr.com/3r2vn

【讨论】:

    【解决方案3】:

    好吧,由于我还没有找到这个问题的令人信服的答案(无意冒犯@Mikael Svenson),我只是坚持我的问​​题中列出的第二种模式。暂时就足够了,因为 tnsnames.ora 文件在我们的组织中始终遵循该确切模式。如果 tnsnames.ora 文件格式发生变化,我很可能会采用解析器。

    【讨论】:

      【解决方案4】:

      此表达式使用 address_list 上的一个地址解析架构,等等。 希望这会有所帮助。

      --开始 (?>((?[\n][\s][^(][\w_.]+)[\s]=[\s]))(?> (?[\n\s(]DESCRIPTION[\s=\s](?>(?[\n\s(]*ADDRESS_LIST[\s=\s]*[\n\ s(]地址[\s=\s](?([\n\s(]社区)([\n\s(]社区[\s=\s]( ?[\w.)]+))|())[\s\n](?([\n\s(]PROTOCOL)([\n\s(]PROTOCOL[\s=\ s](?[\w.)]+))|())[\s\n](?([\n\s(]HOST)([\n\s(]主机[\s=\s](?[\w.)]+))|())[\s\n](?([\n\s(]PORT)([ \n\s(]PORT[\s=\s](?[\w.)]+))|())[\s\n](?())()) |())))[\s\n](?>(?[\n][\s][(]CONNECT_DATA\s*[=]\s*[\n](?([(]SID\s[=]\s*)(([(]SID\s*[=]\s*(?[\w.]+)\s*[ )]))|())[\s\n](?([(]SERVER\s[=]\s*)(([(]SERVER\s*[=]\ s*(?[\w.]+)\s*[)]))|())[\s\n]*(?([(]SERVICE_NAME\s*[=]\s*)(([ (]SERVICE_NAME\s*[=]\s*(?[\w.]+)\s*[)]))|())[\s\n](?())()) |())))[\s\n](?())())|()))) *--结束

      毫无疑问,问题在于以写入该文件的形式出现的倍数。正如您所说的文件必须是唯一的,这可以通过使用 TNS_ADMIN 变量来解决。

      【讨论】:

        【解决方案5】:

        我尝试了上述答案,但没有一个对我有用......

            [DebuggerDisplay("Name {Name} Host:{Host} ServiceName:{ServiceName} Port:{Port}")]
            public class TnsEntry
            {
                public string Name { get; set; }
                public string Host { get; set; }
                public string Port { get; set; }
                public string ServiceName { get; set; }
            }
        
        
           public class TnsNamesFileParser
           {
            public string TNSNamesContents { get; set; }
        
            public TnsNamesFileParser()
            {
            }
        
            public TnsNamesFileParser(string locationAndNameOfTnsNamesFile)
            {
                TNSNamesContents = System.IO.File.ReadAllText(locationAndNameOfTnsNamesFile);
            }
        
            public List<TnsEntry> Parse()
            {
                return Parse(TNSNamesContents);
            }
        
            public List<TnsEntry> Parse(string TNSNamesContents)
            {
                string TNSPattern = @"([\w -] +)\s *= (?:\s |.) +?\)\s *\)\s *\)\s * ((?=[\w\-])|(?=$))";
        
                Regex reTns = new Regex(TNSPattern, RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
                MatchCollection matchCollection = reTns.Matches(TNSNamesContents);
        
                var TnsEntries = new List<TnsEntry>();
        
                foreach (Match match in matchCollection)
                {
                    var tnsEntry = new TnsEntry();
                    string matchedValue = match.Value.Trim();
        
                    tnsEntry.Name = Regex.Match(matchedValue, @"^([^\s]+)", RegexOptions.IgnoreCase)?.Value.Trim();
                    tnsEntry.Host = Regex.Match(matchedValue, "(?<=HOST.+=) ([^)]*)", RegexOptions.IgnoreCase)?.Value.Trim();
                    tnsEntry.Port = Regex.Match(matchedValue, "(?<=PORT.+=) ([^)]*)", RegexOptions.IgnoreCase)?.Value.Trim();
                    tnsEntry.ServiceName = Regex.Match(matchedValue, "(?<=SERVICE_NAME.+=) ([^)]*)", RegexOptions.IgnoreCase)?.Value;
        
                    TnsEntries.Add(tnsEntry);
        
                }
        
                return TnsEntries;
            }
          }
        
        //Test Code: 
        
        string testdata =@"
                SOMESCHEMA =
                (DESCRIPTION =
                (ADDRESS_LIST =
                (ADDRESS = (PROTOCOL = TCP)(HOST = REMOTEHOST)(PORT = 1234))
                )
                (CONNECT_DATA = (SERVICE_NAME = REMOTE)
                )
                )
        
                MYSCHEMA =
                (DESCRIPTION =
                (ADDRESS = (PROTOCOL = TCP)(HOST = MYHOST)(PORT = 1234))
                (CONNECT_DATA =
                (SERVER = DEDICATED)
                (SERVICE_NAME = MYSERVICE.LOCAL )
                )
                )
        
                MYOTHERSCHEMA =
                (DESCRIPTION =
                (ADDRESS_LIST =
                (ADDRESS = (PROTOCOL = TCP)(HOST = MYHOST)(PORT = 1234))
                )
                (CONNECT_DATA = 
                (SERVICE_NAME = MYSERVICE.REMOTE)
                )
                )
        
                SOMEOTHERSCHEMA = 
                (DESCRIPTION =
                (ADDRESS_LIST =
                (ADDRESS = (PROTOCOL = TCP)(HOST = LOCALHOST)(PORT = 1234))
                )
                (CONNECT_DATA =
                (SERVICE_NAME = LOCAL)
                )
                )";
         [Test]
         public void ParseTNSFileEntries()
         {
        
          var tnsNamesFileParser = new TnsNamesFileParser();
          var entries =  tnsNamesFileParser.Parse(testdata);
        
        
         }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-07-08
          • 2012-08-23
          • 2014-06-08
          • 2010-12-09
          相关资源
          最近更新 更多