Antecedência C # ReadLine () para a próxima linha em uma chamada de função
Pergunta
Na minha C # app que eu estou tentando alimentar em ReadLine () um documento de texto simples com 7 cordas dígitos separados linha por linha. O que eu estou tentando fazer é pegar a próxima seqüência de 7 dígitos cada vez que a função é chamada. Aqui está o que eu tenho até agora:
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;
}
Como faço para avançar para a próxima linha de cada vez que o invoiceNumberFunc () é chamado?
Agradecemos antecipadamente.
Solução
Você precisa de manter a preensão do StreamReader
entre chamadas, ou passá-lo para o método como um novo parâmetro ou tornando-se uma variável de membro da classe.
Pessoalmente eu prefiro a idéia de se tornar um parâmetro, para que nunca acaba como uma variável de membro - que faz com que o tempo de vida mais fácil de gerir:
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;
}
Outras dicas
Você não pode, desde que você criar e descartar o leitor de fluxo na função. Duas formas vêm à mente:
Você pode armazenar o leitor de fluxo em uma variável de membro, ou ler tudo de uma vez e armazenar uma matriz em uma variável de membro.
Ou você faz um método iterator alterando o tipo de retorno para IEnumerable<string>
, e mudando a parte no bloco using
a:
while ((invoiceNumber = sr.ReadLine()) != null) {
yield return invoiceNumber;
}
Desta forma, você pode chamar foreach
em seu invoiceNumberFunc
.
Você precisa usar o mesmo StreamReader em vez de criar um novo. Cada vez que você criar um novo e descartar o antigo você está começando para trás logo no início do arquivo.
Tente passar a mesma referência StreamReader ou manter um registro da posição que você está em na corrente e usando Seek () sobre o fluxo de base, se necessário. Eu recomendo o primeiro deles pessoalmente.
Você precisa refazer este, de modo que você não está criando o StreamReader dentro do método, mas em vez de criá-la no nível de classe, e apenas usá-lo no método, em seguida, descartar / fechar o leitor quando você está feito. Algo como:
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;
}
}
}
Neste caso, ele também pode ser uma boa idéia para fazer a sua IDisposable
classe para que você possa verificar se o StreamReader fica disposto, e também, potencialmente, fazer "inicializar" / "fechar" rotinas, em vez de fazer a inicialização e desligamento como eu fiz aqui.
O que você está procurando é o comando 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();
}
}
}
Agora você pode consumir o retorno desta função com um simples para cada um: -
foreach(string invoiceNumber in GetInvoiceNumbers())
{
//Do Stuff with invoice number
}
Ou seja criativo com o LINQ.
Uma outra maneira de fazer isso é transformar a sua função em um bloco de iterator usando o declaração de rendimento retorno
A única coisa é certificar-se de adicionar uma cláusula finalmente ao seu tentar remover a captura como o retorno de rendimento não pode ser usado em uma tentativa nua / catch. Portanto, o seu código seria:
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
{
}
}