使用正则表达式解析tnsnames.ora

时间:2010-08-23 17:02:13

标签: c# .net regex oracle tnsnames

我试图使用正则表达式从我的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)(我在第一个模式中没有)到命名组。 / p>

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

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

5 个答案:

答案 0 :(得分:2)

这应该使用平衡组来完成。并根据您的需要修改开关/外壳。

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

- 开始 ?(大于(([\ n]的[\ s]的 [^(] [\ W _] +)[\ s]的 = [\ s]的))(? &GT;([\ n \ S(说明\ S = \ S] (大于????([\ n \ S(] * ADDRESS_LIST [\ S = \ S] * [ \ n \ S( ADDRESS [\ S = \ S] (?([\ n \ S(]社群)([\ n \ S(]社群[\ S = \ S] < EM>([\瓦特?)] +))|?())[\ S \ n]的((\ n \ S(] PROTOCOL)([\ n \ S(] PROTOCOL [\ S = \ S] ([\ W?)。] +))|?())[\ S \ n]的((\ n \ S(] HOST)([\ n \ S(] HOST [\ S = \ S] ([\ W)] +?))|?())[\ S \ n]的((\ n \ S(] PORT )([\ n \ S(] PORT [\ S = \ S] ([\ w)的。] +?))|?())[\ S \ n]的(()) ())|())))[\ S \ n]的(&gt;有([\ n]的[\ s]的 [(] CONNECT_DATA \ S * [=] \ S * [ \ n]的(?([(] SID \ S [=] \ S *) - (([(] SID \ S * [=] \ S *(?[\瓦特] +) \ S * [)]))|?())[\ S \ n]的(([(] SERVER \ S [=] \ S *) - (([(] SERVER \ S * [=] \ S * \ S * [)]))([\瓦特] +?)|?())[\ S \ n]的*(([(] SERVICE_NAME \ S * [=] \ S * )(([(] SERVICE_NAME \ S * [=] \ S *([\瓦特] +)\ S * [)]))|?())[\ S \ n]的(() )())|())))[\ S \ n]的(())())|?()))) * - 端

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

答案 3 :(得分:0)

以下正则表达式将解析单个TNS条目,您所要做的就是解析每个结果,看你认为哪些名称/值

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

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

答案 4 :(得分:0)

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

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


 }