我正在尝试覆盖StreamReader的ReadLine方法,但由于无法访问某些私有变量而难以执行此操作。这可能,或者我应该编写自己的StreamReader类?
答案 0 :(得分:3)
假设您希望自定义StreamReader可以在任何可以使用TextReader的地方使用,通常有两个选项。
从StreamReader继承并覆盖您希望以不同方式工作的函数。在你的情况下,这将是StreamReader.ReadLine。
继承TextReader并完全根据您的要求实现阅读器功能。
注意:对于上面的选项2,您可以维护对StreamReader实例的内部引用,并将所有函数委派给内部实例,但要替换的功能除外。在我看来,这只是选项2的实现细节,而不是第3个选项。
根据您的问题,我假设您已经尝试过选项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();
}
}
答案 1 :(得分:2)
试试这个,我写这个是因为我有一些非常大的'|'在某些列中包含\ r \ n的分隔文件,我需要使用\ r \ n作为行分隔符的结尾。我试图使用SSIS包导入一些文件,但由于我无法使用的文件中的一些损坏的数据。文件超过5 GB因此太大而无法打开并手动修复。我通过查看大量论坛找到了答案,以了解流如何工作,最终找到了一个解决方案,该解决方案读取文件中的每个字符并根据我添加到其中的定义吐出该行。这是在命令行应用程序中使用,完成帮助:)。我希望这可以帮助其他人,我没有找到像其他地方一样的解决方案,尽管这些想法受到了这个论坛和其他人的启发。这不会修复它只拆分它们的文件......请注意,这仍然是一项正在进行中的工作:)。
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()); } }
答案 2 :(得分:1)
这门课可以帮到你
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;
}
}