Использование одного потока памяти для нескольких итераций поиска файлов
У меня есть несколько методов, каждый из которых применяет операцию к текстовому файлу, где следующая операция требует в качестве ввода результат предыдущей операции:
private TextReader input = new StreamReader("input.txt");
private TextWriter output = new StreamWriter("output.txt");
MemoryStream result_1 = new MemoryStream();
MemoryStream result_2 = new MemoryStream();
Operation_1(input, ref result_1);
Operation_2(result_1, ref result_2);
Operation_3(result_2, output);
Код для Операции_1:
private void Operation_1(TextReader input, ref MemoryStream output)
{
TextWriter outputWriter = new StreamWriter(output);
String line;
while (input.Peek() >= 0) //while not end of file
{
line = input.ReadLine();
//perform operation on line
outputWriter.writeline(line);
}
input.Close();
}
код для операции_2:
private void Operation_2(TextReader input, ref MemoryStream output)
{
input.Seek(0, SeekOrigin.Begin); //reset stream to start of file
TextReader inputReader = new StreamReader(input);
TextWriter outputWriter = new StreamWriter(output);
String line;
while (inputReader.Peek() >= 0) //while not end of file
{
line = inputReader.ReadLine();
//perform operation on line
outputWriter.writeline(line);
}
inputReader.Close();
}
Код для операции_3:
private void operation_3(MemoryStream input, TextWriter output)
{
input.Seek(0, SeekOrigin.Begin); //reset stream to start of file
TextReader inputReader = new StreamReader(input);
String line;
while (inputReader.Peek() >= 0) //while not end of file
{
line = inputReader.ReadLine();
//perform operation on line
output.writeline(line);
}
inputReader.Close();
output.Close();
}
Теперь проблема в том, что я не получаю тот же результат, что и сохраняю каждый промежуточный результат в физическом текстовом файле на жестком диске и использую этот файл для следующей операции. Несколько строк и конец файла отсутствует.
Также это кажется не очень чистым и общим способом сделать это.
Отсюда и мой вопрос; почему мои результаты отличаются при использовании MemoryStream для промежуточных результатов и существует ли более чистый и гибкий способ сделать это? (Я хочу работать над решением, где можно выбрать, хотите ли вы сохранить промежуточные результаты или нет).
2 ответа
- Они разные, потому что вы забыли смыть своих писателей.
ref
модификаторы не нужны, потому что вы не создаете новыйMemoryStream
в ваших операциях
Ваши методы будут немного чище:
private void Operation_1(TextReader input, Stream output)
{
TextWriter outputWriter = new StreamWriter(output);
String line;
outputWriter.Write(input.ReadToEnd());
outputWriter.Flush();
input.Close();
}
private void Operation_2(Stream input, Stream output)
{
input.Seek(0, SeekOrigin.Begin); //reset stream to start of file
TextReader inputReader = new StreamReader(input);
TextWriter outputWriter = new StreamWriter(output);
outputWriter.Write(inputReader.ReadToEnd());
outputWriter.Flush();
inputReader.Close();
}
Код для операции_3:
private void operation_3(Stream input, TextWriter output)
{
input.Seek(0, SeekOrigin.Begin); //reset stream to start of file
TextReader inputReader = new StreamReader(input);
output.Write(inputReader.ReadToEnd());
inputReader.Close();
output.Flush();
output.Close();
}
У меня есть предложение изменить ваш код. Возможно, вместо использования Streams и TextReaders вы могли бы просто работать с IEnumerable.
Пожалуйста, смотрите образец ниже (Это просто образец. Обработка ошибок не включена, чтобы сохранить образец простым).
Результат предыдущей операции предоставляется в качестве параметра для следующего. Следовательно, он выполняется следующим образом: Операция3(Операция2(Операция1))).
Первый пример ниже читает и изменяет строки, содержащиеся в файле, построчно. Второй пример ниже читает весь файл и изменяет строки, предоставляющие все строки для следующей операции (lines.ToArray() читает весь файл).
С потоками вы всегда должны быть осторожны, чтобы они располагались правильно и в нужное время (например, StreamReader закрывает внутренний поток по умолчанию, когда StreamReader удаляется).
using System;
using System.Collections.Generic;
using System.IO;
using System.Globalization;
using System.Linq;
namespace ConsoleApplication2
{
class Program
{
static void Main()
{
//line per line...
File.WriteAllLines
(
@"C:\temp\output.txt",
ChangeLines(File.ReadLines(@"C:\temp\input.txt"),
line =>
LineOperation3
(
LineOperation2
(
LineOperation1(line)
)
)
)
);
//lines per lines...
File.WriteAllLines
(
@"C:\temp\output2WithCount.txt",
ChangeLines(File.ReadLines(@"C:\temp\input.txt"),
lines =>
LinesCountOperation
(
LinesCountOperation
(
LinesCountOperation(lines,LineOperation1),
LineOperation2
)
, LineOperation3
)
)
);
}
private static IEnumerable<string> ChangeLines(IEnumerable<string> lines, Func<string, string> lineFunc)
{
foreach (var line in lines)
{
yield return lineFunc(line);
}
}
private static IEnumerable<string> ChangeLines(IEnumerable<string> lines, Func<IEnumerable<string>, IEnumerable<string>> linesFunc)
{
foreach(var changedLine in linesFunc(lines))
{
if (changedLine != null)
{
yield return changedLine;
}
}
}
private static IEnumerable<string> LinesCountOperation(IEnumerable<string> lines, Func<string, string> lineFunc)
{
var readAllLines = lines.ToArray();
var linesCount = readAllLines.Count();
foreach (var line in readAllLines)
{
var changedLine = lineFunc(line);
if (changedLine == null)
{
continue;
}
yield return string.Format(CultureInfo.InvariantCulture, "{0}-{1}", linesCount, changedLine);
}
}
private static string LineOperation1(string line)
{
return string.Format(CultureInfo.InvariantCulture, "{0}{1}", line, "1");
}
private static string LineOperation2(string line)
{
return string.Format(CultureInfo.InvariantCulture, "{0}{1}", line, "2");
}
private static string LineOperation3(string line)
{
return string.Format(CultureInfo.InvariantCulture, "{0}{1}", line, "3");
}
}
}