تحليل 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 ، ولن يتطابق مع motherchema إذا لم يحتوي على جميع العناصر نفسها.
من الناحية المثالية ، أود أن يكون نمطًا يتطابق مع إدخال 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) ، فقد علقت للتو بالنمط الثاني المدرج في سؤالي. إنه يكفي في الوقت الحالي ، حيث يتبع ملف 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\-])
مثال: https://regexr.com/3r2vn
هذا التعبير تحليل المخطط بعنوان واحد على العنوان ، وما إلى ذلك. آمل أن يساعد هذا.
-begin (؟> ((؟ [ 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*) (([(] 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);
}