我知道我可以使用简单Stream
+ string
组合从MemoryStream
创建StreamWriter
:
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(value);
writer.Flush();
stream.Position = 0;
或在编码上使用GetBytes
:
new MemoryStream(Encoding.UTF8.GetBytes(value ?? ""))
但是,这两个解决方案最终都会将整个字符串重新分配为byte[]
。对于长串而言,这是一个大问题。
有没有办法直接在Stream
上获得string
而无需重新分配整个内容?类似于如何将MemoryStream
包裹在现有byte[]
上。
答案 0 :(得分:1)
有没有办法直接在字符串上获取Stream而无需重新分配整个内容?类似于如何将MemoryStream包装在现有的byte []上。
创建一个自定义流类来包装它。以下是非常粗糙的,但它的表现比我预期的好很多。我只是把它鞭打了,所以它没有太多的测试(即:它适用于我的一次测试运行),但它应该足以显示这个概念。
编辑:我修改了代码,以解决在原始代码无法从多字节字符中读取单个字节的注释中出现的问题。我还添加了指定可选编码器的功能,用于将字符转换为字节。如果未提供编码器,将使用UTF-8。
使用StreamReader对代码进行了最低限度的测试,但不应将其视为可供生产使用。
Imports System.Text
Public Class StringStream : Inherits IO.Stream
Private bm As BufferManager
''' <summary>
''' Creates a non seekable stream from a System.String
''' </summary>
''' <param name="source"></param>
''' <param name="encoding">Default UTF-8</param>
''' <remarks></remarks>
Public Sub New(source As String, Optional encoding As System.Text.Encoding = Nothing)
Me.bm = New BufferManager(source, encoding)
End Sub
Public Overrides ReadOnly Property CanRead As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property CanSeek As Boolean
Get
Return False
End Get
End Property
Public Overrides ReadOnly Property CanWrite As Boolean
Get
Return False
End Get
End Property
Public ReadOnly Property Encoding As System.Text.Encoding
Get
Return bm.Encoding
End Get
End Property
Public Overrides Sub Flush()
End Sub
Public Overrides ReadOnly Property Length As Long
Get
Return 1 'Me.source.Length
End Get
End Property
Public Overrides Property Position As Long
Get
Return Me.bm.Position
End Get
Set(value As Long)
' seek not supported
End Set
End Property
Public Overrides Function ReadByte() As Integer
' Ref: https://msdn.microsoft.com/en-us/library/system.io.stream.readbyte(v=vs.110).aspx
' Reads a byte from the stream and advances the position within the stream by one byte,
' or returns -1 if at the end of the stream.
Dim ret As Int32 = -1
Dim b As Byte
If Me.bm.GetByte(b) Then ret = b
Return ret
End Function
Public Overrides Function Read(buffer() As Byte, offset As Integer, count As Integer) As Integer
' ref: https://msdn.microsoft.com/en-us/library/system.io.stream.read(v=vs.110).aspx
' Return Value: The total number of bytes read into the buffer.
' This can be less than the number of bytes requested if that many bytes are not currently available,
' or zero (0) if the end of the stream has been reached.
Dim maxReturnedCount As Int32 = Math.Min(buffer.Length, count)
Dim returnCount As Int32
For i As Int32 = 0 To maxReturnedCount - 1
If Me.bm.GetByte(buffer(i)) Then
returnCount += 1
Else
Exit For
End If
Next
Return returnCount
End Function
Public Overrides Function Seek(offset As Long, origin As IO.SeekOrigin) As Long
Return -1
End Function
Public Overrides Sub SetLength(value As Long)
End Sub
Public Overrides Sub Write(buffer() As Byte, offset As Integer, count As Integer)
End Sub
Private Class BufferManager
Private buffer As Byte()
Private bufferPosition As Int32
Private source As String
Private positionInSource As Int32
Private _encoding As System.Text.Encoding
Private numBytesInbuffer As Int32
Private readPosition As Int32
Public Sub New(source As String, encoding As System.Text.Encoding)
If encoding Is Nothing Then
encoding = System.Text.Encoding.UTF8
End If
Me.source = source
Me._encoding = encoding
buffer = New Byte(0 To encoding.GetMaxByteCount(1) - 1) {}
End Sub
Public ReadOnly Property HasBytes As Boolean
Get
Return (numBytesInbuffer > 0) OrElse LoadCharToBuffer()
End Get
End Property
Public ReadOnly Property Encoding As System.Text.Encoding
Get
Return Me._encoding
End Get
End Property
Public ReadOnly Property Position As Int32
Get
Return Me.readPosition
End Get
End Property
Private Function LoadCharToBuffer() As Boolean
Dim ret As Boolean
If positionInSource < Me.source.Length Then
Me.numBytesInbuffer = Me._encoding.GetBytes(source, positionInSource, 1, buffer, 0)
Me.positionInSource += 1
Me.bufferPosition = 0
ret = True
End If
Return ret
End Function
Public Function GetByte(ByRef value As Byte) As Boolean
Dim ret As Boolean = Me.HasBytes
If ret Then
value = buffer(bufferPosition)
Me.bufferPosition += 1
Me.numBytesInbuffer -= 1
Me.readPosition += 1
End If
Return ret
End Function
End Class
End Class