【问题标题】:Parse in C# with Dictionary<string, string>使用 Dictionary<string, string> 在 C# 中解析
【发布时间】:2011-05-19 03:34:13
【问题描述】:

我是编程新手,一直在努力解析文件。我最初试图以某种方式解析它,但最终并没有正常工作。我想解析 Dictionary 中的以下行。

网卡:已安装 7 个 NIC。

                       [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

我希望 [01] Broadcom 作为字典的键和连接名称:本地连接 DHCP 已启用:无 IP 地址 [01]:abc.de.xyz 作为值,其他六个以此类推.谢谢您的帮助。真的很感激。任何关于如何去做的帮助都会很棒,因为我疯狂地阅读了关于拆分字符串并试图弄清楚如何让字典来存储值。

【问题讨论】:

  • 是否允许 NIC 卡的名称具有 . (句号)的名字?如果是这样,可能很难区分 [01]: NIC_Name 和 [01]: mno.pqr.stu.vwx。
  • 网卡末尾没有句点。我正在从一个文件中读取这个。我正在阅读它并将文件存储为字符串数组。
  • 你可以正常执行此操作(读取'[]'的行检查并将其放入 dic)
  • 我的意思是,是否有可能在 NIC 卡的名称中包含句点。例如,[01]: Example.Name ... 如果是这样,则很难区分名称和 IP 地址。如果您永远不会在名称中使用句点,那么您可以通过检查是否出现句点来区分网卡名称行和 IP 地址行。
  • @Rev:使用类似于密钥的 _sub_line 是不可能的。例如。 [01]: mno.pqr.stu.vwx

标签: c# .net text-parsing


【解决方案1】:

如果您不想走这条路,这里有一个不使用正则表达式的解决方案。此代码已经过测试。

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】:

    请原谅任何糟糕的 C# 语法 - 我已经习惯了 VB .NET。别笑。

    我会先将文件的文本行读入一个字符串数组。

    foreach (string line in File.ReadLines("path-to-file")) {
    
    }
    

    对于每一行,您要么位于“键”行,要么位于“值”行。关键行如下所示:

    [01]: Broadcom
    

    要确定您是否在“关键”行上,您可以尝试类似line.Trim().StartsWith("[") 的方法,但这不会可靠地工作,因为您还有其他看起来像[01]: abc.def.ghi.jkl 的行是IP 地址,并且不是钥匙。所以你需要更聪明一点,甚至可能使用正则表达式来检测你是否正在查看 IP 地址或网卡。我不知道您正在查看的文件的确切规格,但您也可以使用前导空格/制表符来帮助您确定您是在“键”还是“值”行。

    您的代码将如下所示:

    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() + " ";
      }
    }
    

    IsKeyLine 方法需要编写,是整个操作的关键。以下是您可能会使用的基于正则表达式的方法:

    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;
    }
    

    现在,我没有花时间测试任何代码 - 这完全是我的想法。但它至少应该让你朝着正确的方向前进。不过,要确定的最重要的事情是文件格式的标准。这将为您提供走上正确道路的线索。您甚至可能不需要正则表达式(这会更好,因为正则表达式通常运行起来很昂贵)。

    【讨论】:

    • 我稍微修改了您的 IsKeyLine 方法以确保它可以编译。您需要使正则表达式是逐字字符串或转义每个反斜杠。此外,如果字符串为 null 或空,则当需要 bool 时(或可能引发异常),您没有返回值。我添加了一个 ArgumentNullException。
    • 谢谢杰森。是的,不习惯 C# - 我通常在 VB 中编写代码。我总是忘记逃避反斜杠。虽然我不认为抛出异常是件好事。我将修改它以返回 false。
    【解决方案3】:

    您还可以计算每行开头的制表符/空格,指示该行所属的位置。

    【讨论】:

      【解决方案4】:

      考虑利用 前导空白 来确定该行所扮演的“角色”(嘿,Python 是这样做的 ;-)。然后可以使用简单的状态机逐行解析文件。

      我怀疑,由于这是生成的输出,因此可以可靠地使用此方法。如果是这样的话,就大大简化了规则和解析。

      编码愉快。


      这是一个用于确定生产线“角色”的小型概念验证。

      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】:

        我知道,这个问题是关于 C#,而不是关于 powershell,并且已经有一些好的 C# 答案,我仍然想贡献一个 powershell 解决方案,作为要考虑的事情。它可以证明比 c# 代码更简单,但这取决于观点:

        $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
        

        如果您安装了 powershell(现在它是 Windows 7 的一部分),您只需将其打开并在命令提示符处粘贴上述代码即可立即看到结果。

        【讨论】:

          【解决方案6】:

          如果你把它放在 csv 输出中可能会更容易。

          Systeminfo /fo csv
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-03-01
            • 2013-08-27
            • 1970-01-01
            • 1970-01-01
            • 2011-07-09
            • 2012-09-21
            • 1970-01-01
            • 2014-04-05
            相关资源
            最近更新 更多