Regexを使用してtnsnames.oraを解析します
質問
Regexを使用して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エントリのみに一致するパターンを持ち、ホスト、ポート、サービス名をキャプチャし、オプションで(サーバー=捧げ物)(最初のパターンにはありませんでした)。
以下は、テストに使用しているサンプルの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)、私は質問にリストされている2番目のパターンに固執しました。 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)
)
)
";
}
次のREGEXは個々のTNSエントリを解析します。あなたがしなければならないことは、各結果を解析することです。
([\w-]+)\s*=(?:\s|.)+?\)\s*\)\s*\)\s*(?=[\w\-])
この式は、address_listなどに1つのアドレスでスキーマを解析します。これが役立つことを願っています。
-begin(?>((?[ n] [ s^(] [ w _。]+)[ s= [ s))(?>(?[ n s)説明[ s = s(?>(?[ n s(]*address_list [ s = s]*[ n s)アドレス[ s = s(?([ n s(] community)([ n s(] community [ s = s(?[ w。)]+))|())[ s n(?([ n s(]プロトコル)([ n s(]プロトコル[ s = s(?[ w。)]+))|())[ s n(?([ n s(] host)([ 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*)(([(] server 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);
}