2017-02-04 14 views
1

Я знаю, что может создать 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[].

+1

Вы можете читать символы из строки с помощью StringReader, а затем обрабатывать их (включая вызов Encoding.GetBytes или запись в Stream через StreamWriter). Что именно вы пытаетесь достичь? – Joe

+0

У меня есть собственный десериализационный код, который принимает поток в качестве входных данных. Однако в некоторых случаях данные уже считываются на строку. Я хотел бы десериализовать его, не перераспределяя всю вещь, когда «Stream» переходит в код десериализации. – MarcinJuraszek

+1

Если это внутренний десериализационный код, вы можете добавить перегрузку, которая принимает TextReader? В противном случае вы можете написать собственный класс, полученный из Stream. – Joe

ответ

1

Есть ли способ получить поток непосредственно над строкой, не перераспределяя всю вещь? Подобно тому, как вы можете обернуть MemoryStream поверх существующего байта [].

Создайте собственный класс потока, чтобы его обернуть. Следующий очень груб, но он выполнялся намного лучше, чем я ожидал. Я просто взбивал это, поэтому он не испытывал большого количества испытаний (т. Е. Он работал для моего одного тестового прогона), но этого должно быть достаточно, чтобы показать концепцию.

Редактировать: Я изменил код для решения проблемы, возникшей в комментариях, что исходный код не смог прочитать один байт из многобайтового символа. Я также добавил возможность указать дополнительный кодер для преобразования символов в байты. Если кодер не предусмотрен, используется 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 
+1

Принцип правильный, но пользовательский класс потока должен обрабатывать символы, которые могут генерировать> 1 байт (например, «äβçδèäβçδèäβçδè ...»), что означает, что вам нужен буфер для хранения их между вызовами Read. Чтобы завершить его, вам, вероятно, понадобится конструктор, который принимает кодировку, хотя UTF8 является разумным значением по умолчанию. – Joe

+0

Или на более низком уровне, чем «Кодирование», вы можете напрямую использовать экземпляр ['Encoder'] (https://msdn.microsoft.com/en-us/library/system.text.encoder. ASPX); это базовый класс для того, что превращает символы в байты (что вам нужно, так как 'Stream', в отличие от 'TextReader', обрабатывает байты, а не символы). – stakx

+0

@Joe, код допускает многобайтовые символы. В методе 'Read' байты каждого символа добавляются в буфер, если есть место. Если нет, он возвращает значение read read, меньшее, чем считанные байты запроса. Я протестировал его с вашим примером и другими, и он прошел нормально. Принимая во внимание настройку кодирования, да, и другие функции были бы хороши, но я не был намерен предоставить полнофункциональную утилиту. Эти тонкости легко добавить. – TnTinMn

 Смежные вопросы

  • Нет связанных вопросов^_^