2

在我的 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() 时如何前进到下一行?

提前致谢。

4

6 回答 6

8

您需要保持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;
}
于 2009-09-23T16:15:17.850 回答
2

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

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

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

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

这样,您可以调用foreach您的invoiceNumberFunc.

于 2009-09-23T16:18:25.923 回答
1

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

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

于 2009-09-23T16:14:46.413 回答
1

您需要重新进行此操作,这样您就不会在方法内创建流读取器,而是在类级别创建它,并仅在方法中使用它,然后在完成后处理/关闭读取器。就像是:

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 是否已被释放,并且还可能进行“初始化”/“关闭”例程,而不是像我在这里那样进行初始化和关闭。

于 2009-09-23T16:17:57.423 回答
1

您正在寻找的是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();
        }
    }
}

现在您可以使用每个简单的函数来使用此函数的返回:-

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

或者使用 LINQ 发挥创意。

于 2009-09-23T16:22:33.840 回答
1

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

唯一的事情是确保在 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
        {
        }
    }
于 2009-09-23T16:24:27.860 回答