C#如何解析格式不一致的文本文件,忽略不需要的信息

时间:2015-10-19 01:36:55

标签: c#

一点背景。我不熟悉在专业环境中使用C#。我的经验主要是在SQL中。我有一个文件,我需要解析,以提取某些信息。我可以弄清楚如何解析每一行,但却一直在寻找特定的信息。我对有人为我完成此代码不感兴趣。相反,我感兴趣的是关于我可以从这里走的地方。 以下是我编写的代码示例。

class Program
{
    private static Dictionary<string, List<string>> _arrayLists = new Dictionary<string, List<string>>();
    static void Main(string[] args)
    {
        string filePath = "c:\\test.txt";
        StreamReader reader = new StreamReader(filePath);
        string line;

        while (null !=(line = reader.ReadLine()))
        {
            if (line.ToLower().Contains("disconnected"))
            {
                // needs to continue on search for Disconnected or Subscribed
            }
            else
            {
                if (line.ToLower().Contains("subscribed"))
                {
                    // program needs to continue reading file
                    // looking for and assigning values to
                    // dvd, cls, jhd, dxv, hft

                    // records start at Subscribed and end at ;
                }
            }

        }
    }
}

对该文件的一点解释。我基本上需要提取订阅和第一个词之间存在的数据;我来了。具体来说,我需要获取诸如dvd = 234之类的值,并将它们分配给代码中的相同变量。并非每条记录都有相同的变量。

以下是我需要解析的文本文件示例。

test information
annoying information
Subscribed      more annoying info
            more annoying info

dvd = 234,
cls = 453,
jhd = 567,

more annoying info
more annoying info

dxv = 456,
hft = 876;

more annoying info

test information
annoying information
Subscribed      more annoying info
            more annoying info

dvd = 234,
cls = 455,

more annoying info
more annoying info

dxv = 456,
hft = 876,
jjd = 768;

more annoying info

test information
annoying information
Disconnected        more annoying info
            more annoying info



more annoying info

修改

我对这个模糊的问题道歉。我必须学习如何提出更好的问题。

我的思维过程是确保程序将订阅和;之间的所有细节关联为一条记录。我认为我困惑的部分是阅读线条。在我的脑海中,我看到循环读取订阅的行,然后进入方法并读取下一行并分配值,依此类推,直到它到达;。一旦完成,我试图弄清楚如何告诉程序退出该方法,但继续从分号后的行读取。也许我在想这个。

我会接受我给出的建议,看看我能提出什么来解决这个问题。谢谢。

3 个答案:

答案 0 :(得分:0)

与问题的所有代码解决方案一样,有许多可能的方法可以实现您的目标。有些人会比其他人更好。以下是一种可以帮助您指明正确方向的方法。

  1. 您可以检查字符串是否以关键字或值开头,例如“dvd”(请参阅​​MSDN String.StartsWith)。

  2. 如果是,那么您可以将字符串拆分为一个部分数组(参见MSDN String.Split)。

  3. 然后,您可以使用所需值的索引从字符串数组中获取每个部分的值。

  4. 使用检索到的值执行您需要的操作。

  5. 继续检查每一行的关键业务规则(即将结束该部分的分号)。也许你可以检查字符串的最后一个字符。 (见String.EndsWith)

答案 1 :(得分:0)

从你的问题来看,现在还不清楚你正在努力解决的具体问题。我建议您编辑您的问题,提供您想要克服的具体挑战。目前你的问题陈述是&#34;已经停留在搜索特定的信息片段#34;。这是非特定的。

说过我会尽力帮助你。

首先,你永远不会像if那样进入:

line.ToLower().Contains("Disconnected")

在这里,您将所有字符转换为小写,然后您尝试在其中查找大写为"D"的子字符串。上面的表达式(几乎)总是评估为false。

其次,为了让您的应用程序执行您想要执行的操作,需要跟踪当前的解析状态。我会忽略&#34; Disconnected&#34;现在,你没有表现出它的重要性。

我假设您正在尝试在文件中找到Subscribed和第一个分号之间的所有内容。我还会对可以构成字符串的内容做出其他一些假设,我不会在这里列出。这些可能是错误的,但考虑到您提供的信息,这是我最好的猜测。

您的程序将从状态开始&#34;寻找订阅&#34;。你已经设置了read循环,这很好。在这个循环中,你读取文件的行,你会发现一个包含单词Subscription。

一旦找到这样的行,你的解析器就需要转移到#34;解析订阅&#34;州。在这种状态下,当你读取行时,你会寻找像jjd = 768这样的行,最后可能会用分号。您可以使用正则表达式检查线条是否与图案匹配。

正则表达式还可以将匹配划分为捕获组,以便您可以分别提取名称(jjd)和值(768)。分号的存在或不存在可能是另一个RegEx组。

请注意,RegEx并不是解决此问题的唯一方法,但这是第一个想到的方法。

然后你继续匹配你的正则表达式的行,并提取名称和值,直到你遇到分号,此时你切换回&#34;寻找订阅&#34;状态。

使用当前状态来决定如何处理下一个读取行。

您将一直持续到文件结束。

一般来说,你想阅读解析。

希望这有帮助。

答案 2 :(得分:0)

处理包含半结构化数据的文本文件时,状态变量可以简化算法。在下面的代码中,布尔状态变量isInRecord用于跟踪记录中line的时间。

using System;
using System.Collections.Generic;
using System.IO;

namespace ConsoleApplication19
{
  public class Program
  {
    private readonly static String _testData = @"
test information
annoying information
Subscribed      more annoying info
            more annoying info

dvd = 234,
cls = 453,
jhd = 567,

more annoying info
more annoying info

dxv = 456,
hft = 876;

more annoying info

test information
annoying information
Subscribed      more annoying info
            more annoying info

dvd = 234,
cls = 455,

more annoying info
more annoying info

dxv = 456,
hft = 876,
jjd = 768;

more annoying info

test information
annoying information
Disconnected        more annoying info
            more annoying info



more annoying info";

    public static void Main(String[] args)
    {
      /* Create a temporary file containing the test data. */
      var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Path.GetRandomFileName());
      File.WriteAllText(testFile, _testData);

      try
      {
        var p = new Program();
        var records = p.GetRecords(testFile);

        foreach (var kvp in records)
        {
          Console.WriteLine("Record #" + kvp.Key);
          foreach (var entry in kvp.Value)
          {
            Console.WriteLine("  " + entry);
          }
        }
      }
      finally
      {
        File.Delete(testFile);
      }
    }

    private Dictionary<String, List<String>> GetRecords(String path)
    {
      var results = new Dictionary<String, List<String>>();
      var recordNumber = 0;
      var isInRecord = false;

      using (var reader = new StreamReader(path))
      {
        String line;

        while ((line = reader.ReadLine()) != null)
        {
          line = line.Trim();

          if (line.StartsWith("Disconnected"))
          {
            // needs to continue on search for Disconnected or Subscribed
            isInRecord = false;
          }
          else if (line.StartsWith("Subscribed"))
          {
            // program needs to continue reading file
            // looking for and assigning values to
            // dvd, cls, jhd, dxv, hft

            // records start at Subscribed and end at ;

            isInRecord = true;
            recordNumber++;
          }
          else if (isInRecord)
          {
            // Check if the line has a general format of "something = something".
            var parts = line.Split("=".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length != 2)
              continue;

            // Update the relevant dictionary key, or add a new key.
            List<String> entries;
            if (results.TryGetValue(recordNumber.ToString(), out entries))
              entries.Add(line);
            else
              results.Add(recordNumber.ToString(), new List<String>() { line });

            // Determine if the isInRecord state variable should be toggled.
            var lastCharacter = line[line.Length - 1];
            if (lastCharacter == ';')
              isInRecord = false;
          }
        }
      }

      return results;
    }
  }
}