Fetch particular string from particular block - c#

I am new to c#. I have text file with data in it but I want to read particular block line data.
Here address can occur multiple times in text file.
Something here...
... ... ...
interface "system"
address 10.4.1.10/32
no shutdown
exit
something here...
... ... ...
address 101.4.1.11/32
but i want to capture within this
interface "system"
address 10.4.1.10/32
no shutdown
exit
I want to capture this ip from the block:
10.4.1.10
I tried this code:
int counter = 0;
string line;
// Read the file and display it line by line.
System.IO.StreamReader file = new System.IO.StreamReader("c:\\test.txt");
while((line = file.ReadLine()) != null)
{
Console.WriteLine (line);
counter++;
}
file.Close();
// Suspend the screen.
Console.ReadLine();
Expected Output:
my expected output is to capture the ip address from that block ie.10.4.1.10
that ip is inside "interface system" block.. that makes that address as unique.. as there can be many ips with keyword address. So i want to take address which is inside interface system block.
Please let me know how i can capture particular string from the 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:
Search for the start of the block
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:
Search for the start of the block
Search for the line inside the block which contains "address"
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();

Here is simple LINQ for that:
var textData = File.ReadAllLines("Path goes here");
var address = string.Join("", textData
.SkipWhile(x => !x.Trim().StartsWith($"interface \"system\""))
.SkipWhile(x => !x.Trim().StartsWith($"address"))
.Take(1)).Split("address")[1].Trim();
SkipWhile goes trough string array until it finds line which starts
like: "interface \"system\"".
Second SkipWhile goes trough part after "interface \"system\"" string until
it finds line which starts like: "address".
Then you Take(1) matching line and create string out of it.
Then you use Split to create new array which contains address text
and ip address.
After that you simply take last part of the array.

Related

finding a variable from string input and extracting it with regex c#

~I have a client which is sending the a message to my server and I am trying to get substrings in order to extract them into variables. I want to use regex for this. Although I have no syntax problems, it will not match. This is the message I am sending and my code.
" PUT /John\r\n\r\n
London "
private StreamReader sReader = null;
private StreamWriter sWriter = null;
public SocketClass(Socket s)
{
socket = s;
NetworkStream nStream = new NetworkStream(s);
sReader = new StreamReader(nStream);
sWriter = new StreamWriter(nStream);
startSocket();
}
String txt = "";
while (sReader.Peek() >= 0)
{
txt += sReader.ReadLine() + "\r\n";
}
else if (txt.Contains("PUT"))
{
Console.WriteLine("triggered");
Regex pattern = new Regex(#"PUT /(?<Name>\d+)\r\n\r\n(?<Location>\d+)\r\n");
Match match = pattern.Match(txt);
if (match.Success)
{
String Name = match.Groups["Name"].Value;
String Location = match.Groups["Location"].Value;
Console.WriteLine(Name);
Console.WriteLine(Location);
}
}
The problem seems to be that while your input has alphanumeric characters your regex is looking for \d which are numeric digits. The regex can be easily changed to this to make it work:
Regex pattern = new Regex(#"PUT /(?<Name>.+)\r\n\r\n(?<Location>.+)\r\n");
. represents any character. It may be that you could narrow it down more to say the match has to be alphabetic characters or something else but the above will certainly work for your given input.

Getting Data From IEnumerable

I have text file which contains airport Codes in this format:
"AAA","","Anaa Arpt","PF","","","AAA","2","N","272"
I used a StreamReader to to read the line from file and then I add that line to string list finally I convert that list to IEnumerable type.
Can you please help me how could I get only three values from each line for example
AAA is airportCode
Anna Arpt airport name
PF is country Code
I want to get only these three values from each row.
Please find below the code.
using (StreamReader sr = new StreamReader("C:/AirCodes/RAPT.TXT"))
{
String line;
while ((line = sr.ReadLine()) != null)
{
aircodesFromTravelPort.Add(line);
Console.WriteLine(line);
}
}
var codes = (IEnumerable<String>)aircodesFromTravelPort;
foreach (var aircode in codes)
It seems that you can try using Linq, something like that:
var codes = File
.ReadLines(#"C:/AirCodes/RAPT.TXT")
.Select(line => line.Split(','))
.Select(items => new {
// I've created a simple anonymous class,
// you'd probably want to create you own one
Code = items[0].Trim('"'), //TODO: Check numbers
Airport = items[2].Trim('"'),
Country = items[3].Trim('"')
})
.ToList();
...
foreach(var item in codes)
Console.WriteLine(item);
You'll probably want to make use of String's Split function on each line to get the values into an array.
while ((line = sr.ReadLine()) != null)
{
var values = line.Split(","); // here you have an array of strings containing the values between commas
var airportCode = values[0];
var airportName = values[2];
var airportCountry = values[3];
var airportInfo = airportCode + "," + airportName + "," + airportCountry;
aircodesFromTravelPort.Add(airportInfo );
// what you actually do with the values is up to you, I just tried to make it as close to the original as possible.
Console.WriteLine(airportInfo);
}
Hope this helps!
I like Regex with named groups:
var line = #"""AAA"","""",""Anaa Arpt"",""PF"","""","""",""AAA"",""2"",""N"",""272""";
var pattern = #"^""(?<airportCode>\w+)"",""(\w*)"",""(?<ariportName>[\w\s]+)"",""(?<cuntryCode>\w+)""";
Match match = Regex.Match(line, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
string airportCode = match.Groups["airportCode"].Value;
string ariportName = match.Groups["ariportName"].Value;
string cuntryCode = match.Groups["cuntryCode"].Value;
}

Search rasphone pbk file

In my application I'm using the rasphone function to connect to vpn's When my application launches it gets all the vpn connections in a combobox using this code.
String f = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Microsoft\Network\Connections\Pbk\rasphone.pbk";
if (System.IO.File.Exists(f))
{
List<string> lines = new List<string>();
using (StreamReader r = new StreamReader(f))
{
string line;
while ((line = r.ReadLine()) != null)
{
lines.Add(line);
}
}
foreach (string s in lines)
{
if (s.StartsWith("["))
{
char[] MyChar = { ']' };
string NewString = s.TrimEnd(MyChar);
char[] MyChar2 = { '[' };
string NewString2 = NewString.TrimStart(MyChar2);
comboBox1.Items.Add(NewString2);
}
}
}
else
{
MessageBox.Show("PBK File not found.");
}
comboBox1.Sorted = true;
Now my question is how I can also get the phonenumer= section to display in a textbox or label, so I know what the IP is.
A pbk file looks like this (had to delete some rows), the problem is that I have multiple vpn connections in the pbk file so also multiple phonenumer= entries.
[VPN Name of connection]
Encoding=1
PBVersion=3
Type=2
DEVICE=vpn
PhoneNumber= 0.0.0.0 <- ip address I want to display in a label or textbox.
AreaCode=
CountryCode=0
CountryID=0
UseDialingRules=0
Comment=
FriendlyName=
LastSelectedPhone=0
PromoteAlternates=0
TryNextAlternateOnFail=1
If you are looking for a very simple solution and I understand your question correctly this should do the trick, add the following statement after your current if in your foreach statement
else if(str.Contains("PhoneNumber"))
{
var x = str.Split('=');
if(x.Length > 1)
ip = x[1];
}
Please note that ip is the variable were you would like to store your IP-address.
To answer your question in the comments and assuming that you always have a [VPN-Connection] before each PhoneNumber entry you could write something like this
foreach (string s in lines)
{
if (s.StartsWith("["))
{
char[] MyChar = { ']' };
string NewString = s.TrimEnd(MyChar);
char[] MyChar2 = { '[' };
string NewString2 = NewString.TrimStart(MyChar2);
comboBox1.Items.Add(NewString2);
}
else if (s.Contains("PhoneNumber"))
{
string ip = comboBox1.Items[comboBox1.Items.Count - 1].ToString() + " : ";
var x = s.Split('=');
if (x.Length > 1)
ip += x[1];
}
}
This would get the item that were last added to the combobox and put it before the string of the ip address, still just a simple hack but it is one way to do it.. if you have more advanced needs I would make a class to store the data that you require and the populate the combobox from that.
well. you could read the file contents and use regex to extract it with
PhoneNumber=(?<ip>[^\n]+)
You can p/invoke RasGetEntryProperties passing it the pbk file, or you can simply parse out the value from the text file. Its in INI format and there are many INI File reader classes out there.

Trim a line on a text file from a website after making a list

Overview
Hey, what I am trying to do is create an add-on for a game where when they join it will check on my website a list of names I have in a text file.
Code
public override void Init()
{
Player.Joined += (object sender, PlayerEventArgs e) =>
{
string autokick = new WebClient().DownloadString("http://leeizazombie.cba.pl/server/autokick.txt");
StringReader sr = new StringReader(autokick);
List<string> autokicklist = new List<string>();
while (sr.Peek() != -1)
{
string line = sr.ReadLine();
autokicklist.Add(line);
}
if (autokicklist.Contains(e.Player.name))
{
e.Player.Kick("Auto-Kick: "); //Reason goes in this part after kicking the player.
}
};
}
Problem
I can't figure out how to get the reason part of the text and exclude the reason part when searching each line, This is an example of a line:
LeeIzaZombie+ : Kicked for a test run.
Where LeeIzaZombie+ is the username and "Kicked for a test run." is the reason.
If the message always contains a ':' after username you can Split it and get the reason like this:
e.Player.Kick("Auto-Kick: {0}", line.Split(':')[1].Trim());
This should work as expected if the reason doesn't contain any :. If it does you can try:
var reason = string.Join(":", line.Split(':').Skip(1));
e.Player.Kick("Auto-Kick: {0}", reason);
Update: in order to access line from if statement you should declare it outside of the loop:
string line = "";
while (sr.Peek() != -1)
{
line = sr.ReadLine();
autokicklist.Add(line);
}

StreamReader case sensitive

My program currently reads a text file and compares it with the value in a text box and then tells me how many matches, this currently works.
My query is that it is case sensitive. Is there any way to make it so it doesn't matter whether it is in upper or lower case?
This is my code below:
if (!String.IsNullOrEmpty(CustodianEAddress.Text))
{
for (AddressLength1 = 0; AddressLength1 < Length; AddressLength1++)
{
List<string> list1 = new List<string>();
using (StreamReader reader = new StreamReader(FileLocation))
{
string line1;
//max 500
string[] LineArray1 = new string[500];
while ((line1 = reader.ReadLine()) != null)
{
list1.Add(line1); // Add to list.
if (line1.IndexOf(cust1[AddressLength1].ToString()) != -1)
{
count1++;
LineArray1[count1] = line1;
}
}
reader.Close();
using (System.IO.StreamWriter filed =
new System.IO.StreamWriter(FileLocation, true))
{
filed.WriteLine("");
filed.WriteLine("The email address " +
cust1[AddressLength1].ToString() + " was found " + count1 +
" times within the recipient's inbox");
}
string count1a;
count1a = count1.ToString();
}
}
}
else
{
MessageBox.Show("Please Enter an Email Address");
}
So basically, I need to compare the value in cust1[AddressLength1] with any values found in an array which is in the text file.
String.Compare() takes in an optional parameter that let's you specify whether or not the equality check should be case sensitive.
Edited in response to code being posted
Compare and Index of both take in an optional enumeration, StringComparison. If you choose StringComparison.OrdinalIgnoreCase then case will be ignored.
Here's a quick way to compare two strings without checking case:
string a;
string b;
string.Compare(a, b, true);
The true here is passed as the value of the ignoreCase parameter, meaning that upper and lower-case letters will be compared as if they were all the same case.
EDIT:
I've cleaned up your code a bit, and also put in the compare function. I included comments where I changed stuff:
// Not needed: see below. List<string> list1 = new List<string>();
using (StreamReader reader = new StreamReader(FileLocation))
{
string line1;
//max 500
List<string> LineArray1 = new List<string>();
while ((line1 = reader.ReadLine()) != null)
{
// list1.Add(line1); // Add to list.
// By adding to the list, then searching it, you are searching the whole list for every single new line - you're searching through the same elements multiple times.
if (string.Compare(line1, cust1[AddressLength1].ToString(), true) == 0)
{
// You can just use LineArray1.Count for this instead. count1++;
LineArray1.Add(line1);
}
}
// Not needed: using() takes care of this. reader.Close();
using (System.IO.StreamWriter filed =
new System.IO.StreamWriter(FileLocation, true))
{
filed.WriteLine(); // You don't need an empty string for a newline.
filed.WriteLine("The email address " +
cust1[AddressLength1].ToString() + " was found " + LineArray1.Count +
" times within the recipient's inbox");
}
string count1a;
count1a = LineArray1.Count.ToString();
}
The fact you are reading from a file or not it does not matter, when compare
use the static string Comapare function:
public static int Compare(
string strA,
string strB,
bool ignoreCase
)
and pass true as a last parameter.

Categories