I am attempting to pull some information from my tnsnames file using regex. I started with the following pattern:
MYSCHEMA *? = *?[\\W\\w\\S\\s]*\\(HOST *?= *
This expression parse the schema with one address on address_list, etc. Hope this helps.
--begin (?>((?[\n][\s][^(][\w_.]+)[\s]=[\s]))(?>(?[\n\s(]DESCRIPTION[\s=\s](?>(?[\n\s(]*ADDRESS_LIST[\s=\s]*[\n\s(]ADDRESS[\s=\s](?([\n\s(]COMMUNITY)([\n\s(]COMMUNITY[\s=\s](?[\w.)]+))|())[\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](?>(?[\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](?())())|()))) *--end
Undoubtedly, the problem is the multiple that is in the form of writing that file. As you say the file must be unique, this is solved by using the TNS_ADMIN variable.
The following regex will parse out individual TNS entries, and all you have to do is parse each result how you see fit for the names / values
([\w-]+)\s*=(?:\s|.)+?\)\s*\)\s*\)\s*(?=[\w\-])
Example: https://regexr.com/3r2vn
This should do it, using balanced groups. And modify the switch/case for your needs.
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)
)
)
";
}
Well, since I haven't found a compelling answer to this issue (no offense @Mikael Svenson), I have just stuck with the second pattern listed in my question. It is sufficient for the time being, as the tnsnames.ora file always follows that exact pattern within our organization. Should the tnsnames.ora file format change, I will most likely adopt a parser.
I tried the above answers and none of them worked for me ...
[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);
}