【问题标题】:Advance C# ReadLine() to next line in a function call将 C# ReadLine() 推进到函数调用中的下一行
【发布时间】:2009-09-23 16:11:50
【问题描述】:

在我的 C# 应用程序中,我试图向 ReadLine() 提供一个简单的文本文档,其中包含 7 个数字字符串逐行分隔。我试图做的是每次调用函数时获取下一个 7 位字符串。到目前为止,这是我所拥有的:

string invoiceNumberFunc()
    {
        string path = @"C:\Users\sam\Documents\GCProg\testReadFile.txt";
        try
        {
            using (StreamReader sr = new StreamReader(path))
            {
                invoiceNumber = sr.ReadLine();

            }

        }
        catch (Exception exp)
        {
            Console.WriteLine("The process failed: {0}", exp.ToString());
        }
       return invoiceNumber;
    }

每次调用 invoiceNumberFunc() 时如何前进到下一行?

提前致谢。

【问题讨论】:

    标签: c# readline


    【解决方案1】:

    您需要在调用之间保留StreamReader,要么将其作为新参数传递给方法,要么使其成为类的成员变量。

    我个人更喜欢将它变成一个参数的想法,这样它就永远不会成为一个成员变量——这使得生命周期更容易管理:

    void DoStuff()
    {
        string path = @"C:\Users\sam\Documents\GCProg\testReadFile.txt";
        using (StreamReader sr = new StreamReader(path))
        {
            while (keepGoing) // Whatever logic you have
            {
                string invoice = InvoiceNumberFunc(sr);
                // Use invoice
            }
        }
    }
    
    string InvoiceNumberFunc(TextReader reader)
    {
        string invoiceNumber;
        try
        {
            invoiceNumber = reader.ReadLine();
        }
        catch (Exception exp)
        {
            Console.WriteLine("The process failed: {0}", exp.ToString());
        }
        return invoiceNumber;
    }
    

    【讨论】:

      【解决方案2】:

      您不能,因为您在函数中创建和处置流阅读器。我想到了两种方法:

      您可以将流读取器存储在成员变量中,或者一次读取所有内容并将数组存储在成员变量中。

      或者通过将返回类型更改为IEnumerable<string> 并将using 块中的部分更改为:

      while ((invoiceNumber = sr.ReadLine()) != null) {
          yield return invoiceNumber;
      }
      

      这样,您可以在您的invoiceNumberFunc 上拨打foreach

      【讨论】:

      • 从文件中读取行的迭代器块是我最喜欢的模式之一。
      【解决方案3】:

      您需要使用相同的 StreamReader 而不是创建一个新的。每次您创建一个新文件并处理掉旧文件时,您都会从文件的开头重新开始。

      尝试在流中传递相同的 StreamReader 引用或记录您在流中的位置,并在必要时在基本流上使用 Seek()。我个人推荐第一个。

      【讨论】:

        【解决方案4】:

        您需要对其进行修改,这样您就不会在方法内创建流读取器,而是在类级别创建它,然后在方法中使用它,然后在完成后处理/关闭读取器。比如:

        class MyClass
        {
            private StreamReader sr;
        
            string invoiceNumberFunc()
            {
                if (sr == null) 
                    sr = new StreamReader(path);
        
                if (sr.EndOfStream)  {
                    sr.Close();
                    sr = null;
                    return string.Empty;
                }
        
                try {
                    return sr.ReadLine();
                }
                catch(Exception exp) {
                    Console.WriteLine("Process failed {0}",exp.ToString());
                    return string.Empty;
                }
            }
        }
        

        在这种情况下,将您的类设置为IDisposable 也是一个好主意,这样您就可以验证 StreamReader 是否已被释放,并且还可能生成“初始化”/“关闭”例程,而不是执行初始化和关闭我在这里的表现。

        【讨论】:

          【解决方案5】:

          您正在寻找的是yield 命令:-

          IEnumerable<string> GetInvoiceNumbers()
          {
              string path = @"C:\Users\sam\Documents\GCProg\testReadFile.txt";
              using (StreamReader sr = new StreamReader(path))
              {
                  while (!sr.EndOfStream)
                  {
                     yield return sr.ReadLine();
                  }
              }
          }
          

          现在你可以用一个简单的 for each 来消费这个函数的返回:-

          foreach(string invoiceNumber in GetInvoiceNumbers())
          {
             //Do Stuff with invoice number
          }
          

          或者使用 LINQ 发挥创意。

          【讨论】:

            【解决方案6】:

            另一种方法是使用 yield return statement 在迭代器块中转换您的函数

            唯一的事情是确保在 try 中添加 finaly 子句并删除 catch,因为 yield return 不能用于裸 try / catch。所以你的代码会变成:

                IEnumerable<String> invoiceNumberFunc()
                {
                    string path = @"C:\Users\sam\Documents\GCProg\testReadFile.txt";
                    try
                    {
                        using ( System.IO.StreamReader sr = new System.IO.StreamReader( path ) )
                        {
                            String invoiceNumber;
                            while ( ( invoiceNumber = sr.ReadLine() ) != null )
                            {
                                yield return sr.ReadLine();
                            }
                        }
                    }
                    finally
                    {
                    }
                }
            

            【讨论】:

            • 嗯,那为什么不把try finally 完全去掉呢?在这种情况下它没有任何好处。
            猜你喜欢
            • 2014-03-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-12-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-24
            相关资源
            最近更新 更多