Переопределить метод ReadLine StreamReader - PullRequest
1 голос
/ 13 июня 2011

Я пытаюсь переопределить метод ReadLine StreamReader, но у меня возникают трудности из-за невозможности доступа к некоторым закрытым переменным.Возможно ли это, или мне просто написать свой собственный класс StreamReader?

Ответы [ 3 ]

3 голосов
/ 13 июня 2011

Если вы хотите, чтобы ваш пользовательский StreamReader мог использоваться везде, где можно использовать TextReader, обычно есть два варианта.

  1. Наследуйте от StreamReader и переопределяйте функции, которые вы хотите, чтобы они работали по-другому. В вашем случае это будет StreamReader.ReadLine.

  2. Наследуйте от TextReader и полностью реализуйте функциональность считывателя в соответствии с вашими требованиями.

Примечание: для варианта 2 выше вы можете сохранить внутреннюю ссылку на экземпляр StreamReader и делегировать все функции внутреннему экземпляру, кроме части функциональности, которую вы хотите заменить. На мой взгляд, это просто детали реализации варианта 2, а не третьего варианта.

Исходя из вашего вопроса, я предполагаю, что вы пробовали вариант 1 и обнаружили, что переопределение StreamReader.ReadLine довольно сложно, поскольку вы не можете получить доступ к внутренним компонентам класса. Хорошо, что для StreamReader вам повезло, и вы можете достичь этого, не имея доступа к внутренней реализации StreamReader.

Вот простой пример:

Отказ от ответственности: реализация ReadLine() предназначена для демонстрационных целей и не предназначена для надежной или полной реализации.

class CustomStreamReader : StreamReader
{
  public CustomStreamReader(Stream stream)
    : base(stream)
  {
  }

  public override string ReadLine()
  {
    int c;

    c = Read();
    if (c == -1)
    {
      return null;
    }

    StringBuilder sb = new StringBuilder();
    do
    {
      char ch = (char)c;
      if (ch == ',')
      {
        return sb.ToString();
      }
      else
      {
        sb.Append(ch);
      }
    } while ((c = Read()) != -1);
    return sb.ToString();
  }
}

Вы заметите, что я просто использовал метод StreamReader.Read () для чтения символов из потока. Хотя метод Read() определенно меньше по формуле, чем работа напрямую с внутренними буферами, он все же использует внутреннюю буферизацию, поэтому все равно должен давать довольно хорошую производительность, но это следует проверить, чтобы подтвердить.

Для забавы, вот пример варианта 2. Я использовал инкапсулированный StreamReader, чтобы уменьшить реальный код, это вообще не проверялось.

class EncapsulatedReader : TextReader
{
  private StreamReader _reader;

  public EncapsulatedReader(Stream stream)
  {
    _reader = new StreamReader(stream);      
  }

  public Stream BaseStream
  {
    get
    {
      return _reader.BaseStream;
    }
  }

  public override string ReadLine()
  {
    int c;

    c = Read();
    if (c == -1)
    {
      return null;
    }
    StringBuilder sb = new StringBuilder();
    do
    {
      char ch = (char)c;
      if (ch == ',')
      {
        return sb.ToString();
      }
      else
      {
        sb.Append(ch);
      }
    } while ((c = Read()) != -1);
    return sb.ToString();
  }

  protected override void Dispose(bool disposing)
  {
    if (disposing)
    {
      _reader.Close();
    }
    base.Dispose(disposing);
  }

  public override int Peek()
  {
    return _reader.Peek();
  }

  public override int Read()
  {
    return _reader.Read();
  }

  public override int Read(char[] buffer, int index, int count)
  {
    return _reader.Read(buffer, index, count);
  }

  public override int ReadBlock(char[] buffer, int index, int count)
  {
    return _reader.ReadBlock(buffer, index, count);
  }

  public override string ReadToEnd()
  {
    return _reader.ReadToEnd();
  }

  public override void Close()
  {
    _reader.Close();
    base.Close();
  }
}
2 голосов
/ 28 сентября 2012

Попробуйте, я написал это, потому что у меня есть очень большой '|' файлы с разделителями, которые имеют \ r \ n внутри некоторых столбцов, и мне нужно было использовать \ r \ n в качестве конца строки. Я пытался импортировать некоторые файлы, используя пакеты служб SSIS, но из-за некоторых поврежденных данных в файлах я не смог. Файл был более 5 ГБ, поэтому он был слишком большим, чтобы открыть и исправить вручную. Я нашел ответ, просмотрев множество форумов, чтобы понять, как работают потоки, и в итоге нашел решение, которое считывает каждый символ в файле и выплевывает строку на основе определений, которые я добавил в него. это для использования в приложении командной строки, в комплекте с помощью :). Я надеюсь, что это поможет другим людям, я не нашел такого решения, как где-либо еще, хотя идеи были вдохновлены этим форумом и другими. Это не исправит файлы, а только разбивает их ... имейте в виду, что это все еще в стадии разработки:).

    class Program
    {
        static long _fileposition = 0;

        static void Main(string[] args)
        {

            // Check information passed in
            if (args.Any())
            {
                if (args[0] == "/?")
                {
                    var message = "Splits a file into smaller pieces";
                    message += "\n";
                    message += "\n";
                    message += "SplitFile [sourceFileName] [destinationFileName] [RowBatchAmount] [FirstRowHasHeader]";
                    message += "\n";
                    message += "\n";
                    message += "     [sourceFileName]  (STRING) required";
                    message += "\n";
                    message += "     [destinationFileName]  (STRING) will default to the same location as the sourceFileName";
                    message += "\n";
                    message += "     [RowBatchAmount]   (INT) will create files that have this many rows";
                    message += "\n";
                    message += "     [FirstRowHasHeader]    (True/False) Will Add Header Row to each new file";
                    Console.WriteLine(message);
                }
                else
                {
                    string sourceFileName = args[0];
                    string destFileLocation = args.Count() >= 2 ? args[1] : sourceFileName.Substring(0, sourceFileName.LastIndexOf("\\"));
                    int RowCount = args.Count() >= 3 ? int.Parse(args[2]) : 500000;
                    bool FirstRowHasHeader = true;
                    FirstRowHasHeader = args.Count() != 4 || bool.Parse(args[3]);

                    // Create Directory If Needed
                    if (!Directory.Exists(destFileLocation))
                    {
                        Directory.CreateDirectory(destFileLocation);
                    }

                    string line = "";
                    int linecount = 0;
                    int FileNum = 1;
                    string newFileName = Path.Combine(destFileLocation, Path.GetFileNameWithoutExtension(sourceFileName));
                    newFileName += FileNum + Path.GetExtension(sourceFileName);

                    // Always add Header Line
                    string HeaderLine = GetLine(sourceFileName, _fileposition);
                    int HeaderCount = HeaderLine.Split('|').Count();

                    do
                    {
                        // Add Header Line
                        if ((linecount == 0 & FirstRowHasHeader) | (_fileposition == 1 & !FirstRowHasHeader))
                        {
                            using (FileStream NewFile = new FileStream(newFileName, FileMode.Append))
                            {
                                System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
                                Byte[] bytes = encoding.GetBytes(HeaderLine);
                                int length = encoding.GetByteCount(HeaderLine);
                                NewFile.Write(bytes, 0, length);
                            }
                        }

                        //Evaluate Line
                        line = GetLine(sourceFileName, _fileposition, HeaderCount);

                        if (line == null) continue;

                        // Create File if it doesn't exist and write to it
                        using (FileStream NewFile = new FileStream(newFileName, FileMode.Append))
                        {
                            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
                            Byte[] bytes = encoding.GetBytes(line);
                            int length = encoding.GetByteCount(line);
                            NewFile.Write(bytes, 0, length);
                        }

                        //Add to the line count
                        linecount++;

                        //Create new FileName if needed
                        if (linecount == RowCount)
                        {
                            FileNum++;
                            // Create a new sub File, and read into it
                            newFileName = Path.Combine(destFileLocation, Path.GetFileNameWithoutExtension(sourceFileName));
                            newFileName += FileNum + Path.GetExtension(sourceFileName);
                            linecount = 0;
                        }
                    } while (line != null);
                }
            }
            else
            {
                Console.WriteLine("You must provide sourcefile!");
                Console.WriteLine("use /? for help");
            }
        }

        static string GetLine(string sourceFileName, long position, int NumberOfColumns = 0)
        {
            byte[] buffer = new byte[65536];
            var builder = new StringBuilder();
            var finishedline = false;

            using (Stream source = File.OpenRead(sourceFileName))
            {
                source.Position = position;
                var crlf = "\r\n";
                var lf = "\n";
                var length = source.Length;

                while (source.Position = 0 & finishedline == false &  _fileposition = NumberOfColumns) | NumberOfColumns == 0)
                                            {
                                                // Remove all Control Line Feeds before the end of the line.
                                                builder = builder.Replace(crlf, lf);

                                                // Add Final Control Line Feed
                                                var x = (char)NewLine.Read();
                                                builder.Append(x);
                                                finishedline = true;
                                                _fileposition++;
                                                continue;
                                            }
                                        }

                                        break;
                                    }
                                default:
                                    builder.Append(c);
                                    break;
                            }
                        }
                    }
                    break;
                }
            }

            return (builder.ToString() == "" ? null: builder.ToString());
        }
    }

Рекомендации: http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/b0d4cba1-471a-4260-94c1-fddd4244fa23/

этот помог мне больше всего: https://stackoverflow.com/a/668003/1582188

1 голос
/ 13 июня 2011

этот класс может вам помочь

public class MyStreamReader : System.IO.StreamReader
{
    public MyStreamReader(string path)
        : base(path)
    {

    }
    public override string ReadLine()
    {
        string result = string.Empty;
        int b = base.Read();
        while ((b != (int)',') && (b > 0))
        {
            result += this.CurrentEncoding.GetString(new byte[] { (byte)b });
            b = base.Read();
        }
        return result;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...