【问题标题】:C# Template parsing and matching with text fileC#模板解析与文本文件匹配
【发布时间】:2011-09-22 16:44:43
【问题描述】:

需要一些想法来解决这个问题。 我有一个模板文件,它描述了文本文件中的行。例如:

模板

[%f1%]|[%f2%]|[%f3%]"[%f4%]"[%f5%]"[%f6%]

文本文件

1234|1234567|123"12345"12"123456

现在我需要从文本文件中读取字段。在模板文件中的字段用[%some name%] 描述。同样在模板文件中设置了字段分隔符,在这个例子中这里有|"。字段的长度可以通过不同的文件更改,但分隔符将保持不变。在模板中读取和在文本文件中读取模板的最佳方法是什么?

编辑:文本文件有多行,如下所示:

1234|1234567|123"12345"12"123456"\r\n
1234|field|123"12345"12"asdasd"\r\n
123sd|1234567|123"asdsadf"12"123456"\r\n
45gg|somedata|123"12345"12"somefield"\r\n

EDIT2:好的,让它变得更难。有些字段可以包含二进制数据,我知道二进制数据字段的开始和结束位置。我应该能够在模板中标记这些字段,然后解析器就会知道这个字段是二进制的。如何解决这个问题?

【问题讨论】:

  • 字段值是否仅为十进制数字?
  • @HalfTrackMindMan:不,字段值可以是任何东西,有时甚至是二进制。

标签: c# templates


【解决方案1】:

1- 使用 sscanf(line, format, __arglist) 的 API 检查 here

2- 使用字符串拆分 Like:

public IEnumerable<int> GetDataFromLines(string[] lines)
{ 
    //handle the output data
    List<int> data = new List<int>();

    foreach (string line in lines)
    {
        string[] seperators = new string[] { "|", "\"" };

        string[] results = line.Split(seperators, StringSplitOptions.RemoveEmptyEntries);

        foreach (string result in results)
        {
            data.Add(int.Parse(result));
        }
    }

    return data;
}

用线测试它:

line = "1234|1234567|123\"12345\"12\"123456";
string[] lines = new string[] { line };

GetDataFromLines(lines);

//output list items are:
1234
1234567
123
12345
12
123456

【讨论】:

  • @hs2d: 你有没有像上面的例子一样尝试过 string.Split?
  • 这对我不起作用,因为在解析文本文件后,我需要知道字段的顺序以及它们之间的分隔符。
【解决方案2】:

我会根据模板创建一个正则表达式,然后使用它解析文本文件:

class Parser
{
    private static readonly Regex TemplateRegex =
        new Regex(@"\[%(?<field>[^]]+)%\](?<delim>[^[]+)?");

    readonly List<string> m_fields = new List<string>();
    private readonly Regex m_textRegex;

    public Parser(string template)
    {
        var textRegexString = '^' + TemplateRegex.Replace(template, Evaluator) + '$';
        m_textRegex = new Regex(textRegexString);
    }

    string Evaluator(Match match)
    {
        // add field name to collection and create regex for the field
        var fieldName = match.Groups["field"].Value;
        m_fields.Add(fieldName);
        string result = "(.*?)";

        // add delimiter to the regex, if it exists
        // TODO: check, that only last field doesn't have delimiter
        var delimGroup = match.Groups["delim"];
        if (delimGroup.Success)
        {
            string delim = delimGroup.Value;
            result += Regex.Escape(delim);
        }
        return result;
    }

    public IDictionary<string, string> Parse(string text)
    {
        var match = m_textRegex.Match(text);
        var groups = match.Groups;

        var result = new Dictionary<string, string>(m_fields.Count);

        for (int i = 0; i < m_fields.Count; i++)
            result.Add(m_fields[i], groups[i + 1].Value);

        return result;
    }
}

【讨论】:

  • 这是个好主意,但我不确定它是否适合我。
  • @svick,我的意思是我已经尝试适应,然后我可以判断它是否适合我。但我认为这是最好的主意。
  • @svick,如果我在一开始就告诉我在文本文件中有多行,你将如何改变逻辑?我在原始帖子中添加了一个包含多行的新示例。
  • @hs2d,我不会改变它,但我会称之为File.ReadAllLines(path).Select(parser.Parse)。该表达式返回一个字典序列,每行一个字典。
  • @svick,我很抱歉,但我看不出这是如何工作的?也许你可以向我解释一下?因为我认为 parser.Parse 需要一行一行的字符串,但在评论中的示例中你没有给它任何东西?
【解决方案3】:

您可以使用正则表达式解析模板。像这样的表达式将匹配每个字段定义和分隔符:

Match m = Regex.Match(template, @"^(\[%(?<name>.+?)%\](?<separator>.)?)+$")

匹配将包含(名称和分隔符)的两个命名组,每个组将在输入字符串中每次匹配时包含许多捕获。在您的示例中,分隔符组的捕获量将比名称组少一个。

然后您可以遍历捕获,并使用结果从输入字符串中提取字段并存储值,如下所示:

if( m.Success )
{
    Group name = m.Groups["name"];
    Group separator = m.Groups["separator"];
    int index = 0;
    Dictionary<string, string> fields = new Dictionary<string, string>();
    for( int x = 0; x < name.Captures.Count; ++x )
    {
        int separatorIndex = input.Length;
        if( x < separator.Captures.Count )
            separatorIndex = input.IndexOf(separator.Captures[x].Value, index);
        fields.Add(name.Captures[x].Value, input.Substring(index, separatorIndex - index));
        index = separatorIndex + 1;
    }
    // Do something with results.
}

显然,在实际程序中,您必须考虑无效输入等问题,我在这里没有这样做。

【讨论】:

  • 我不太擅长使用正则表达式,但您尝试过该正则表达式是否可以工作吗?
【解决方案4】:

我会用几行代码来做到这一点。遍历模板行,将“[”之间的所有文本作为变量名,将其他所有文本作为终止符。将所有文本读取到终端,将其分配给变量名,重复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 2021-10-06
    • 2012-01-03
    • 2015-01-07
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多