【问题标题】:C#: What's an efficient way of parsing a string with one delimiter through ReadLine() of TextReader?C#:通过TextReader的ReadLine()解析带有一个分隔符的字符串的有效方法是什么?
【发布时间】:2010-03-04 23:53:55
【问题描述】:

C#:为 TextReader 的每个 ReadLine() 解析带有一个分隔符的字符串的有效方法是什么?

我的目标是将 ListView 的代理列表加载到从 .txt 文件读取的两列(代理|端口)中。我将如何使用分隔符“:”将每个 readline() 拆分为代理和端口变量?

这就是我到目前为止所得到的,

    public void loadProxies(string FilePath)
    {
        string Proxy; // example/temporary place holders
        int Port; // updated at each readline() loop.

        using (TextReader textReader = new StreamReader(FilePath))
        {
            string Line;
            while ((Line = textReader.ReadLine()) != null)
            {
                // How would I go about directing which string to return whether
                // what's to the left of the delimiter : or to the right?
                //Proxy = Line.Split(':');
                //Port = Line.Split(':');

                // listview stuff done here (this part I'm familiar with already)
            }
        }
    }

如果没有,有没有更有效的方法来做到这一点?

【问题讨论】:

    标签: c# listview split


    【解决方案1】:
    string [] parts = line.Split(':');
    string proxy = parts[0];
    string port = parts[1];
    

    【讨论】:

      【解决方案2】:

      你可以这样拆分它们:

              string line;
              string[] tokens;
              while ((Line = textReader.ReadLine()) != null)
              {
                  tokens = line.Split(':');
                  proxy = tokens[0];
                  port = tokens[1];
      
                  // listview stuff done here (this part I'm familiar with already)
              }
      

      最好在 C# 中为变量使用小写字母名称,因为其他变量是为类/命名空间名称等保留的。

      【讨论】:

        【解决方案3】:

        对整个文件运行正则表达式怎么样?

        var parts=
            Regex.Matches(input, @"(?<left>[^:]*):(?<right>.*)",RegexOptions.Multiline)
            .Cast<Match>()
            .Where(m=>m.Success)
            .Select(m => new
                {
                    left = m.Groups["left"],
                    right = m.Groups["right"]
                });
        
        foreach(var part in parts)
        {
            //part.left
            //part.right
        }
        

        或者,如果它太大,为什么不使用 yielding 方法 Linqify ReadLine 操作?

        static IEnumerable<string> Lines(string filename)
        {
            using (var sr = new StreamReader(filename))
            {
                while (!sr.EndOfStream)
                {
                    yield return sr.ReadLine();
                }
            }
        }
        

        然后像这样运行它:

        var parts=Lines(filename)
        .Select(
            line=>Regex.Match(input, @"(?<left>[^:]*):(?<right>.*)")
        )
        .Where(m=>m.Success)
        .Select(m => new
            {
                left = m.Groups["left"],
                right = m.Groups["right"]
            });
        foreach(var part in parts)
        {
            //part.left
            //part.right
        }
        

        【讨论】:

        • 请注意,如果您使用的是 .NET 4.0,则无需创建 Lines 方法。
        • 很高兴知道。没有时间用 .net4 弄脏。
        • 对于一个相当简单的操作,我个人认为这过于复杂。我仍在为其 +1,因为这是一个很好的答案。
        • linq 不酷吗?我一直在寻找新颖的方法将所有内容变成一个序列!
        • 我注意到您的腰带下没有 F# 标签。如果你喜欢序列,你可能应该纠正它。
        【解决方案4】:

        效率而言,我预计您将很难被击败:

            int index = line.IndexOf(':');
            if (index < 0) throw new InvalidOperationException();
            Proxy = line.Substring(0, index);
            Port = int.Parse(line.Substring(index + 1));
        

        这避免了与Split 关联的数组构造/分配,并且只查看第一个分隔符。但我应该强调的是,除非数据量 巨大,否则这不太可能成为真正的性能瓶颈,因此几乎任何方法都应该没问题。事实上,也许最重要的事情(我被下面的评论提醒过)是在添加时暂停 UI:

        myListView.BeginUpdate();
        try {
            // TODO: add all the items here
        } finally {
            myListView.EndUpdate();
        }
        

        【讨论】:

        • 是的,毫无疑问,ListView 将成为瓶颈,在您能够分辨 SplitSubstring 之间的区别之前。
        • @gabe - 好点;添加更新以使ListView 更快。
        【解决方案5】:

        您可能想尝试这样的事情。

        var items = File.ReadAllText(FilePath)
            .Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)
            .Select(line => line.Split(':'))
            .Select(pieces => new { 
                Proxy = pieces[0], 
                Port = int.Parse(pieces[1]) 
            });
        

        如果您知道文件末尾不会有杂散的换行符,您可以这样做。

        var items = File.ReadAllLines(FilePath)
            .Select(line => line.Split(':'))
            .Select(pieces => new { 
                Proxy = pieces[0], 
                Port = Convert.ToInt32(pieces[1]) 
            });
        

        【讨论】:

        • 请记住,使用ReadAllTextReadAllFiles 会立即将整个文件读入内存。在这里不太可能很重要,但总的来说,最好使用像支出者答案中的迭代器。
        • @gabe - 你是对的,我想他们会想看看他们所有的选择。
        • 没有理由不显示选项,只要确保注意缺点。用户正在寻找最有效的方法,而 ReadAll 只对小文件有效。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-02-03
        • 1970-01-01
        • 1970-01-01
        • 2016-04-17
        • 2011-08-22
        • 2012-04-12
        • 2013-03-08
        相关资源
        最近更新 更多