问题
I am new to programming and have been trying hard to parse a file. I, initially was trying to parse it in a certain way, but that didn't end up working correctly. I want to parse the following line in a Dictionary< string,string> .
Network Card(s): 7 NIC(s) Installed.
[01]: Broadcom
Connection Name: Local Area Connection
DHCP Enabled: No
IP address(es)
[01]: abc.de.xyz.
[02]: Broadcom
Connection Name: eth1
Status: Media disconnected
[03]: Broadcom
Connection Name: eth0
Status: Media disconnected
[04]: Broadcom
Connection Name: eth3
Status: Media disconnected
[05]: Mellanox
Connection Name: Local Area Connection 5
Status: Hardware not present
[06]: Mellanox
Connection Name: Local Area Connection 6
Status: Media disconnected
[07]: Mellanox
Connection Name: Local Area Connection 7
DHCP Enabled: No
IP address(es)
[01]: mno.pqr.stu.vwx
I want [01] Broadcom as the key to the dictionary and Connection Name: Local Area Connection DHCP Enabled: No IP address(es) [01]: abc.de.xyz as the value and so on for the other six. Thanks for the help. Really appreciate it. Any help on how to go about doing it will be great since I have gone crazy reading about splitting strings and trying to figure out how to get the dictionary to store the value.
回答1:
Here is a solution that does not use regex if you don't want to take that route. This code has been tested.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace NicParser
{
public class NicFileParser
{
private readonly string _file;
private readonly Dictionary<string, string> _nics;
public NicFileParser(string file)
{
_file = file;
_nics = new Dictionary<string, string>();
}
public void Parse()
{
var key = string.Empty;
var value = new StringBuilder();
try
{
using (var rdr = new StreamReader(_file))
{
var firstTime = true;
while (rdr.Peek() > 0)
{
var line = rdr.ReadLine().Trim();
if (IsKey(line))
{
// Once a key is hit, add the previous
// key and values (except the first time).
if (!firstTime)
{
_nics.Add(key, value.ToString());
}
else
{
firstTime = false;
}
// Assign the key, and clear the previous values.
key = line;
value.Length = 0;
}
else
{
// Add to the values for this nic card.
value.AppendLine(line);
}
}
// Final line of the file has been read.
// Add the last nic card.
_nics.Add(key, value.ToString());
}
}
catch (Exception ex)
{
// Handle your exceptions however you like...
}
}
private static bool IsKey(string line)
{
return (!String.IsNullOrEmpty(line)
&& line.StartsWith("[")
&& !line.Contains("."));
}
// Use this to access the NIC information.
public Dictionary<string, string> Cards
{
get { return _nics; }
}
}
}
回答2:
Forgive any poor C# syntax - I'm used to VB .NET. Don't laugh.
I would read the file's lines of text into a string array first.
foreach (string line in File.ReadLines("path-to-file")) {
}
For each line, you're either on a "key" line or a "value" line. Key lines look like this:
[01]: Broadcom
To determine if you are on a "key" line, you could try something like line.Trim().StartsWith("[")
, but that won't work reliably because you've got other lines which look like [01]: abc.def.ghi.jkl
which are IP addresses, and are not keys. So you need to be a little smarter about it and possibly even use a regular expression to detect if you are looking at an IP address or a network card. I don't know the exact specifications of the file you are looking at, but you also could use the leading spaces/tabs to help you determine whether or not you are on a "key" or a "value" line.
Your code would then look something like this:
var networkCards = new Dictionary<String, String>();
string currentKey = String.Empty;
foreach (string line in File.ReadLines("path-to-file")) {
if ( IsKeyLine( line ) ) {
currentKey = line.Trim();
networkCards.Add(currentKey, "");
} else {
networkCards[currentKey] += line.Trim() + " ";
}
}
The IsKeyLine
method would need to be written, and is the crux of the whole operation. Here's a stab at a regular expression based method you might use:
public bool IsKeyLine(string line) {
if (!String.IsNullOrEmpty(line)) {
//run two regexes - one to see if the line is of the general pattern of a "key" line
//the second reg ex makes sure there isn't an ip address in the line, which would indicate that the line is part of the "value" and not the "key"
return System.Text.RegularExpressions.RegEx.IsMatch(line, @"^\s*\[\d{0,2}\]: ")
&& !System.Text.RegularExpressions.RegEx.IsMatch(line, @"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}");
}
return false;
}
Now, I did not take the time to test any of this code - this is off the top of my head. But it should at least get you going in the general right direction. The biggest thing to determine is the standard for the file format though. That will give you clues to go down the right path. You might not even need regular expressions (which would be preferable, since regex's are typically expensive to run).
回答3:
You could also count the tabs/whitespaces at the beginning of each line, indicating where the line belongs to.
回答4:
Consider utilizing the leading white space to determine the "role" the line plays (hey, Python does ;-). Then the file can be parsed line-by-line using a simple state machine.
I suspect that, since this is generated output, this method can be used reliably. If this is the case, it greatly simplifies the rules and parsing.
Happy coding.
Here is a small proof-of-concept to determine the "role" of the line.
using (var inp = ...) {
string line;
while ((line = inp.ReadLine()) != null) {
// normalize to our world of 8-space tabs
line = line.Replace("\t", " ");
var lineDepth = line.Length - line.TrimStart().Length;
if (lineDepth < 65) {
// is potential "heading line"
} else { // >= 65
// is "property line"
}
}
}
回答5:
I know, that this question is about C#, not about powershell, and there are already a few good C# answers, still I would like to contribute a powershell solution, as something to consider. It can turn out to be simpler than c# code, but this depends on the point of view:
$networkCards = systeminfo | ForEach-Object {$a=0} {
if ($_.startswith("Network Card(s)")) {$a=1} else {if ($a) {$_}}
}
$networkCards | ForEach-Object {$data=@{}} {
if ($_.trim().startswith("[")) {
$c = $_.trim(); $data[$c] = @()} else {$data[$c] += $_.trim()
}
}
#Now we have a hash table with the keys as requested in the question
#and the values are lists of separate strings, but those can be easily
#concatenated if needed. Let's display it:
$data
If you have powershell installed (It's part of Windows 7 now) you just can open it and paste the above code at the command prompt and you will be able immediately to see the result.
回答6:
Might make it easier if you put it in a csv output.
Systeminfo /fo csv
来源:https://stackoverflow.com/questions/6053447/parse-in-c-sharp-with-dictionarystring-string