Fetch particular string from particular block

Regular Expressions are perfectly suited to handle this type of "problem". The following console app demonstrates how to use Regex to extract the desired IP address from the targeted string block.

private static readonly string IPV4_PATTERN = "[0-9./]";
private static readonly string IPV4_IPV6_PATTERN = "[A-Z0-9:./]";

static void Main(string[] args)
{
    TestSearchFile();
}  

private static string ParseIpWithRegex(string textToSearch, string startBlock, string endBlock)
{
    var pattern = $@"{startBlock}\D*\s*({IPV4_IPV6_PATTERN}+).*{endBlock}";
    var ms = Regex.Match(textToSearch, pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
    if (ms.Groups.TryGetValue("1", out var g))
    {
        return g.Value;
    }
        return string.Empty;
    }

private static void TestSearchFile()
{
    var sep = Environment.NewLine;

    var ipAddress6 = "2001:db8:85a3:8d3:1319:8a2e:370:7348";
    var ipAddress4 = "10.4.1.10/32";

    var t = "Something here..." + sep;
    t += "... ... ... " + sep;
    t += "interface \"system\"" + sep;
    t += "address " + ipAddress4 + sep;
    t += "no shutdown" + sep;
    t += "exit" + sep;
    t += "something here..." + sep;
    t += "address 101.4.1.11/32" + sep;
    t += "... ... ... " + sep;

    var startBlock = "interface \"system\"";
    var endBlock = "exit";

    var ip = ParseIpWithRegex(t, startBlock, endBlock);
    Console.WriteLine($"IP: {ip}");
}

I've included two IP address patterns IPV4_PATTERN for IPV4 only as well as IPV4_IPV6_PATTERN for both IPV4 and IPV6. Select the one you feel is most appropriate. Although the IPV4_IPV6_PATTERN would apply to both IP versions I believe it improves performance slight when the search is narrowed by using the narrowest pattern.

Don't forget to import the Regex reference:

using System.Text.RegularExpressions;


**Code Explained**

The method "ParseIpWithRegex" uses a Regex pattern constructed by using the string that signifies the start of the targeted block and the string that signifies the end of that block. Nestled within that pattern is the regular expressions class definition that defines the IP address pattern we wish to isolate as a group.

$@"{startBlock}\D*\s*({IPV4_IPV6_PATTERN}+).*{endBlock}";

It should be noted that the curly brackets are just for string interpolation and have (in this case) nothing to do with the actual regular expression!

After the "startBlock" we see "\D*". This means that after the "startBlock" include in the search all non-numeric characters (where the "star" indicates to expect zero to infinitely many). Then we see "\s*" which means to include all white space (including new line characters since I included RegexOptions.Singleline).

The IP address pattern is in brackets "()" which instructs Regex to create groups. In this case, behind the IP address pattern (in the above code example IPV4_IPV6_PATTERN) there is a "+" symbol. This indicates that there MUST be at least one of the characters that is in the IP address Regex class definition in order to be considered a "match".

After that we see ".*" in front of the "endBlock". This means to look for any character--including the "new line" character (zero to infinitely many) in from of the "endBlock" string.

If you have any questions, please leave a comment.


EDIT

From your button onclick method you will call SearchFileForIp. You will need to change myTextBox to match your code.

You should also decide whether you will be searching IPV4 or both IPV4 and IPV6 and select the appropriate variable IPV4_PATTERN or IPV4_IPV6_PATTERN.

private void SearchFileForIp()
{
    var fileName = "c:\\test.txt";
    using var sr = new StreamReader(fileName);
    string fileContent = sr.ReadToEnd();

    var startBlock = "interface \"system\"";
    var endBlock = "exit";

    var ip = ParseForIpRegex(fileContent, startBlock, endBlock);
    myTextBox.Text = ip; //Change this to match your code
}

private readonly string IPV4_PATTERN = "[0-9./]";
private readonly string IPV4_IPV6_PATTERN = "[A-Z0-9:./]";
private string ParseForIpRegex(string textToSearch, string startBlock, string endBlock)
{
    var pattern = $@"{startBlock}\D*\s*({IPV4_PATTERN}+).*{endBlock}";
    var ms = Regex.Match(textToSearch, pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
    if(ms.Groups.Count > 0)
    {
        return ms.Groups[1].Value;
    }

    //For .Net Core apps
    //if (ms.Groups.TryGetValue("1", out var g))
    //{
    //    return g.Value;
    //}

    return string.Empty;
}

In addition to the 2 answers with Regex solutions, If address line comes always after interace "system", than a simple for loop can do the job.

interface "system"
    address 10.4.1.10/32       
    no shutdown
exit

So We go thorugh file lines and check if line is interace "system" than take the next value and parse it to string of ip address.

public static string GetIpAddressFromFile(string fileName, string startLine)
{
    var lines = File.ReadAllLines(fileName);
    var ipAddress = string.Empty;

    for (var i = 0; i < lines.Length; i++)
    {
        var line = lines[i].Trim();

        if (line != startLine) continue;
        var addressLine = lines[i + 1].Trim().Replace("address", "");
        ipAddress = addressLine.Substring(0, addressLine.IndexOf("/", StringComparison.Ordinal));
        break;
    }

    return ipAddress.Trim();
}

Lets assume you that your file is inconsistent and address does not comes first after interface "system"

interface "system"
    ...
    address 10.4.1.10/32       
    no shutdown
exit

So in this case we put all lines between interface "system" and exit in list of strings, Or dictionary and fetch the address key.

public static string GetIpAddressFromFile(string fileName, string startLine, string endLine)
{
    var lines = File.ReadAllLines(fileName);
    var ipAddress = string.Empty;
    var state = false;
    var results = new Dictionary<string, string>();

    foreach (var t in lines)
    {
        var line = t.Trim();
        if (line == startLine)
            state = true;

        if (line == endLine)
            state = false;

        if (!state) continue;
        var s = line.Split(" ");
        results.TryAdd(s[0], s[1]);
    }

    var result = results.GetValueOrDefault("address");
    if (result != null)
    {
        ipAddress = result.Substring(0, result.IndexOf("/", StringComparison.Ordinal));
    }

    return ipAddress;
}

Usage:

var startLine = "interface \"system\"";
var endLine = "exit";
var ip = GetIpAddressFromFile(@"File.txt", startLine);
//Or
var ip = GetIpAddressFromFile1(@"File.txt", startLine, endLine);

Both methods are tested with your given example and return:

10.4.1.10

If the start of the block and the end of the block are well defined, in order to find the block you can simply:

  1. Search for the start of the block
  2. Do something with the lines until the end of the block

string line;
System.IO.StreamReader file = new System.IO.StreamReader("c:\\test.txt");

while((line = file.ReadLine()) != null && !line.Equals(START_OF_BLOCK)); // 1.

while((line = file.ReadLine()) != null && !line.Equals(END_OF_BLOCK)) // 2.
{
    // do something with the lines
}

file.Close();

Updated answer after edited question:

In order to "extract" the string in a form of an IP address inside the block, you could, for example, use Regular expressions with a .NET Regex class, with previously finding the needed block:

  1. Search for the start of the block
  2. Search for the line inside the block which contains "address"
  3. Extract the IP address from the line using Regexp.Match()

string line;
System.IO.StreamReader file = new System.IO.StreamReader("c:\\test.txt");
string pat = @"\b(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\b";
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(pat);

while ((line = Console.ReadLine()) != null && !line.Equals(START_OF_BLOCK)); // 1.
while ((line = Console.ReadLine()) != null && !line.Equals(END_OF_BLOCK)) // 2.
{
    if (line.Contains("address"))
    {
        System.Text.RegularExpressions.Match ip = reg.Match(line);
        Console.WriteLine(ip);
        break; // break if you are sure there's only one ip in that block
    }
}

file.Close();

Tags:

C#