Разборные TNSNAMES.ORA С помощью Regex
Вопрос
Я пытаюсь вытащить некоторую информацию из моего файла TNSNAMES, используя Regex. Я начал со следующего рисунка:
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, и она не будет соответствовать My WeathesChema, если она не содержит всех тех же элементов.
В идеале я хотел бы шаблон, который соответствует только вводам MySchema, а также захватывает имя, порт, порт и сервис, а также необязательно (Server = выделенный) (который у меня не было в первом шаблоне) к названным группам.
Ниже приведен образец 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\-])
Пример: https://regexr.com/3r2vn.
Это выражение разбирает схему с одним адресом по адресу_List и т. Д. Надеюсь, что это поможет.
--бегин (?> ((? [ N] [ S^ (] [ W _.] +) [ s= [ S)) (?> (? [ n s (Описание [ s = s(?> (? [ n s (] * address_list [ s = s] * [ n s (Адрес [ s = s(? ([ n S (] сообщество) ([ N S (] сообщество [ s = s(? [ W)]) +)) | ()) [ s n(? ([ n S (] протокол) ([ n s (] протокол [ s = s(? [ W)]) +)) | ()) [ s n(? ([ n S (] хост) ([ n s (] хост [ 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);
}