使用正则表达式解析 tnsnames.ora
题
我正在尝试使用正则表达式从我的 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 条目匹配的模式,并捕获主机、端口和服务名称,以及可选的 (SERVER = DEDICATED)(我在第一个模式中没有)到命名组。
以下是我用于测试的示例 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)
)
)
解决方案 3
好吧,由于我还没有找到这个问题的令人信服的答案(无意冒犯@Mikael Svenson),所以我只是坚持问题中列出的第二种模式。暂时就足够了,因为 tnsnames.ora 文件始终遵循我们组织内的确切模式。如果 tnsnames.ora 文件格式发生变化,我很可能会采用解析器。
其他提示
使用平衡的组应该做到这一点。并为您的需求修改开关/情况。
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)
)
)
";
}
以下正则将解析单个TNS条目,您要做的就是分析每个结果,您如何看待名称 /值
([\w-]+)\s*=(?:\s|.)+?\)\s*\)\s*\)\s*(?=[\w\-])
此表达式在adversy_list等上用一个地址等地解析了模式。希望这会有所帮助。
-begin(?>((?[ n] [ s])^(] [ w_。]+)[ s= [ s)(?>(?[ n s(])描述[ s = s(?>(?[ n s(]*advelly_list [ s = s]*[ n s(])地址[ s = s(?([ n s(]社区)([ n s(] community [ s = s(?[ w。)]+))|()[ s n(?([ n s(]协议)([ n s(]协议[ s = s(?[ w。)]+))|()[ s n(?([ n s(]主机)([ n s(] host [ s = s(?[ w。)]+))|()[ s n(?([ n s(]端口)([ n s(]端口[ s = s(?[ w。)]+))|()[ s n(?())()())|()))[ s n(?>(?[ n] [ s(] connect_data s*[=] s*[ n(?([(] sid s)=] s*)(([(] sid s*[=] s*(?[ w。]+)+s*[)]))|())[ s n(?([(]服务器 s)=] s*)(([(]服务器 s*[=] s*(?[ w。]+)+s*[)]))|()[ s n]* (?([(] service_name s*[=] s*)(([(] Service_name s*[=] s*(?[w。]+)+s*[)))| ())[ s n(?())()())|()))[ s n(?())()())|())) * - 结束
毫无疑问,问题是以编写该文件的形式的倍数。正如您所说,文件必须是唯一的,这是通过使用TNS_ADMIN变量来解决的。
我尝试了上述答案,它们都不对我有用。
[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);
}