【问题标题】:yield pattern, state machine flow产量模式,状态机流程
【发布时间】:2012-06-21 22:28:32
【问题描述】:

我有以下文件,我正在使用迭代器块来解析文件中某些重复出现的节点/部分。我最初使用正则表达式来解析整个文件,但是当节点中不存在某些字段时,它将不匹配。所以我正在尝试使用收益模式。文件格式如下与我使用的代码一致。我从文件中想要的只是将复制节点作为单独的部分,因此我可以使用密钥字符串获取其中的字段并存储在对象集合中。我可以在第一次复制发生的地方开始解析,但无法在复制节点结束的地方结束。

文件格式:

X_HEADER
{
    DATA_MANAGEMENT_FIELD_2     NA
    DATA_MANAGEMENT_FIELD_3     NA
    DATA_MANAGEMENT_FIELD_4     NA
    SYSTEM_SOFTWARE_VERSION     NA
}
Y_HEADER
{
    DATA_MANAGEMENT_FIELD_2     NA
    DATA_MANAGEMENT_FIELD_3     NA
    DATA_MANAGEMENT_FIELD_4     NA
    SYSTEM_SOFTWARE_VERSION     NA
}
COMPLETION
{
    NUMBER          877
    VERSION         4
    CALIBRATION_VERSION 1
    CONFIGURATION_ID    877    
}
REPLICATE
{
    REPLICATE_ID            1985
    ASSAY_NUMBER            656
    ASSAY_VERSION           4
    ASSAY_STATUS            Research
    DILUTION_ID         1
}
REPLICATE
{
    REPLICATE_ID            1985
    ASSAY_NUMBER            656
    ASSAY_VERSION           4
    ASSAY_STATUS            Research
}

代码:

static IEnumerable<IDictionary<string, string>> ReadParts(string path)
{
    using (var reader = File.OpenText(path))
    {
        var current = new Dictionary<string, string>();
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            if (string.IsNullOrWhiteSpace(line)) continue;

            if (line.StartsWith("REPLICATE"))
            {
                yield return current;
                current = new Dictionary<string, string>();
            }
            else
            {
                var parts = line.Split('\t');
            }

            if (current.Count > 0) yield return current;
        }
    }
}

public static void parseFile(string fileName)
    {
        foreach (var part in ReadParts(fileName))
        {
           //part["fIELD1"] will retireve certain values from the REPLICATE PART HERE
        }
    }

【问题讨论】:

  • 您是否忽略了输入的 X_HEADER、Y_HEADER 和 COMPLETION 部分?
  • 是的,我想忽略除复制节点之外的所有内容...

标签: c# .net linq yield


【解决方案1】:

好吧,听起来您只需要在获得右大括号时“关闭”一个部分,并且此时只需 yield return。例如:

static IEnumerable<IDictionary<string, string>> ReadParts(string path)
{
    using (var reader = File.OpenText(path))
    {
        string currentName = null;
        IDictionary<string, string> currentMap = null;
        while ((line = reader.ReadLine()) != null)
        {
            if (string.IsNullOrWhiteSpace(line))
            {
                continue;
            }
            if (line == "{")
            {
                if (currentName == null || currentMap != null)
                {
                    throw new BadDataException("Open brace at wrong place");
                }
                currentMap = new Dictionary<string, string>();
            }
            else if (line == "}")
            {
                if (currentName == null || currentMap == null)
                {
                    throw new BadDataException("Closing brace at wrong place");
                }
                // Isolate the "REPLICATE-only" requirement to a single
                // line - if you ever need other bits, you can change this.
                if (currentName == "REPLICATE")
                {
                    yield return currentMap;
                }
                currentName = null;
                currentMap = null;
            }
            else if (!line.StartsWith("\t"))
            {
                if (currentName != null || currentMap != null)
                {
                    throw new BadDataException("Section name at wrong place");
                }
                currentName = line;
            }
            else
            {
                if (currentName == null || currentMap == null)
                {
                    throw new BadDataException("Name/value pair at wrong place");
                }
                var parts = line.Substring(1).Split('\t');
                if (parts.Length != 2)
                {
                    throw new BadDataException("Invalid name/value pair");
                }
                currentMap[parts[0]] = parts[1];
            }                
        }
    }
}

说实话,现在这是一个非常可怕的功能。我怀疑我会把它放在它自己的类中(可能是嵌套的)来存储状态,并使每个处理程序都有自己的方法。哎呀,这实际上是状态模式可能有意义的情况:)

【讨论】:

  • 看起来没问题。当然,我不知道这个方法属于哪个类,我同意把它放在一个专门用于解析它的类中。但我认为这没关系。
  • @ivowiblo:我不认为这完全可以 - 老实说,much 太长了,不能成为一个理智的方法。但足以作为引领重构之路的答案;)
  • 感谢您的输入,我正在尝试测试,var currentMap 不能设置为空。应该是某种类型吗?
【解决方案2】:
private IEnumerable<IDictionary<string, string>> ParseFile(System.IO.TextReader reader)
{
    string token = reader.ReadLine();

    while (token != null)
    {
        bool isReplicate = token.StartsWith("REPLICATE");
        token = reader.ReadLine(); //consume this token to either skip it or parse it

        if (isReplicate)
        {     
            yield return ParseBlock(ref token, reader);
        }
    }
}

private IDictionary<string, string> ParseBlock(ref string token, System.IO.TextReader reader)
{
    if (token != "{")
    {
        throw new Exception("Missing opening brace.");
    }

    token = reader.ReadLine();

    var result = ParseValues(ref token, reader);

    if (token != "}")
    {
        throw new Exception("Missing closing brace.");
    }

    token = reader.ReadLine();

    return result;
}

private IDictionary<string, string> ParseValues(ref string token, System.IO.TextReader reader)
{
    IDictionary<string, string> result = new Dictionary<string, string>();

    while (token != "}" and token != null)
    {
        var args = token.Split('\t');

        if (args.Length < 2)
        {
            throw new Exception();
        }

        result.Add(args[0], args[1]);

        token = reader.ReadLine();
    }

    return result;
}

【讨论】:

  • 好吧,我对此进行了测试,它不会超过第一行。嗯,再试一次
【解决方案3】:

如果您在 while 循环结束后添加 yield return current;,您将获得最终字典。

我相信最好检查'}'作为当前块的结尾,然后将yield return放在那里。尽管您不能使用正则表达式来解析整个文件,但您可以使用正则表达式来搜索行内的键值对。以下迭代器代码应该可以工作。它只会返回 REPLICATE 块的字典。

 // Check for lines that are a key-value pair, separated by whitespace.
// Note that value is optional
static string partPattern = @"^(?<Key>\w*)(\s+(?<Value>\.*))?$";

static IEnumerable<IDictionary<string, string>> ReadParts(string path)
{
    using (var reader = File.OpenText(path))
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            // Ignore lines that just contain whitespace
            if (string.IsNullOrWhiteSpace(line)) continue; 

            // This is a new replicate block, start a new dictionary
            if (line.Trim().CompareTo("REPLICATE") == 0)
            {
                yield return parseReplicateBlock(reader);
            }
        }
    }
}

private static IDictionary<string, string> parseReplicateBlock(StreamReader reader)
{
    // Make sure we have an opening brace
    VerifyOpening(reader);
    string line;
    var currentDictionary = new Dictionary<string, string>();
    while ((line = reader.ReadLine()) != null)
    {
        // Ignore lines that just contain whitespace
        if (string.IsNullOrWhiteSpace(line)) continue;

        line = line.Trim();

        // Since our regex used groupings (?<Key> and ?<Value>), 
        // we can do a match and check to see if our groupings 
        // found anything. If they did, extract the key and value. 
        Match m = Regex.Match(line, partPattern);
        if (m.Groups["Key"].Length > 0)
        {
            currentDictionary.Add(m.Groups["Key"].Value, m.Groups["Value"].Value);
        }
        else if (line.CompareTo("}") == 0)
        {
            return currentDictionary;
        }
    }
    // We exited the loop before we found a closing brace, throw an exception
    throw new ApplicationException("Missing closing brace");
}

private static void VerifyOpening(StreamReader reader)
{
    string line;
    while ((line = reader.ReadLine()) != null)
    {
        // Ignore lines that just contain whitespace
        if (string.IsNullOrWhiteSpace(line)) continue;

        if (line.Trim().CompareTo("{") == 0)
        {
            return;
        }
        else
        {
            throw new ApplicationException("Missing opening brace");
        }
    }
    throw new ApplicationException("Missing opening brace");
}

更新:我确保正则表达式字符串包含没有值的情况。此外,组索引全部更改为使用组名称,以避免修改正则表达式字符串时出现任何问题。

【讨论】:

  • 如果我的一些值是空的,但密钥存在,例如 AssayNumer,正则表达式不会计算密钥。我正在尝试这样做,以便如果有一个键,那么它应该匹配并且如果值不存在则为空白,我将其用作正则表达式静态字符串 partPattern = @"^(?\w+)\s+( ?.+)$";对此有何想法?
  • 好的,它需要两个小的更新,一个是使值可选,另一个是更新使用键的逻辑。我将更新解决方案以同时进行这两项更改。
  • 我会将您的答案标记为正确。我将正则表达式更改为静态字符串 partPattern = @"^(?\w+)\s*(?.*)$";所以它允许日期或空值。它运行良好
  • 我做了一个快速测试,它应该返回没有指定值的情况。如果您想更改正则表达式字符串,您可能应该将索引组的行转换为使用字符串索引器,而不是像我所做的那样使用 int 索引器。否则,如果您更改哪些组与哪些数字索引匹配,您会得到有趣的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多