我在字符串中有一些数据。我有一个函数,它将流作为输入。我想将我的数据提供给我的函数,而不必将完整的字符串复制到流中。基本上我正在寻找一个可以包装字符串并从中读取的流类。
我在网上看到的唯一建议是建议不是流的StringReader,或者创建一个内存流并写入它,这意味着复制数据。我可以编写自己的流对象,但棘手的部分是处理编码,因为流以字节为单位进行处理。有没有办法在不编写新流类的情况下执行此操作?
我正在BizTalk中实现管道组件。 BizTalk完全使用流来处理所有内容,因此您总是将内容传递给BizTalk。 BizTalk将始终以小块读取该流,因此将整个字符串复制到流中是没有意义的(特别是如果字符串很大),如果我可以从流中读取BizTalk想要它。
您可以防止必须维护整个事物的副本,但是您将被迫使用对每个字符产生相同字节数的编码。这样你可以通过Encoding.GetBytes(str, strIndex, byteCount, byte[], byteIndex)
提供数据块,因为它们被直接请求到读缓冲区。
每个Stream.Read()
操作总会有一个复制操作,因为它允许调用者提供目标缓冲区。
这是一个适当的StringReaderStream
有以下缺点:
Read
的缓冲区必须至少为maxBytesPerChar
。通过保持内部一个char Read
,可以为小缓冲区实现buff = new byte[maxBytesPerChar]
。但对大多数用法来说并不是必需的。Seek
,它可以做寻求,但通常会非常棘手。 (有些寻求案例,如寻求开始,寻求结束,实施起来很简单。)/// <summary>
/// Convert string to byte stream.
/// <para>
/// Slower than <see cref="Encoding.GetBytes()"/>, but saves memory for a large string.
/// </para>
/// </summary>
public class StringReaderStream : Stream
{
private string input;
private readonly Encoding encoding;
private int maxBytesPerChar;
private int inputLength;
private int inputPosition;
private readonly long length;
private long position;
public StringReaderStream(string input)
: this(input, Encoding.UTF8)
{ }
public StringReaderStream(string input, Encoding encoding)
{
this.encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
this.input = input;
inputLength = input == null ? 0 : input.Length;
if (!string.IsNullOrEmpty(input))
length = encoding.GetByteCount(input);
maxBytesPerChar = encoding == Encoding.ASCII ? 1 : encoding.GetMaxByteCount(1);
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => length;
public override long Position
{
get => position;
set => throw new NotImplementedException();
}
public override void Flush()
{
}
public override int Read(byte[] buffer, int offset, int count)
{
if (inputPosition >= inputLength)
return 0;
if (count < maxBytesPerChar)
throw new ArgumentException("count has to be greater or equal to max encoding byte count per char");
int charCount = Math.Min(inputLength - inputPosition, count / maxBytesPerChar);
int byteCount = encoding.GetBytes(input, inputPosition, charCount, buffer, offset);
inputPosition += charCount;
position += byteCount;
return byteCount;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
Stream
只能复制数据。此外,它处理byte
s,而不是char
s,所以你必须通过解码过程复制数据。但是,如果要将字符串视为ASCII字节流,可以创建一个实现Stream
的类来执行此操作。例如:
public class ReadOnlyStreamStringWrapper : Stream
{
private readonly string theString;
public ReadOnlyStreamStringWrapper(string theString)
{
this.theString = theString;
}
public override void Flush()
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
switch (origin)
{
case SeekOrigin.Begin:
if(offset < 0 || offset >= theString.Length)
throw new InvalidOperationException();
Position = offset;
break;
case SeekOrigin.Current:
if ((Position + offset) < 0)
throw new InvalidOperationException();
if ((Position + offset) >= theString.Length)
throw new InvalidOperationException();
Position += offset;
break;
case SeekOrigin.End:
if ((theString.Length + offset) < 0)
throw new InvalidOperationException();
if ((theString.Length + offset) >= theString.Length)
throw new InvalidOperationException();
Position = theString.Length + offset;
break;
}
return Position;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
return Encoding.ASCII.GetBytes(theString, (int)Position, count, buffer, offset);
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { return theString.Length; }
}
public override long Position { get; set; }
}
但是,避免“复制”数据需要做很多工作......