将 Flow<Byte> 转换为 Flow<String> 在 Kotlin 中

huangapple go评论59阅读模式
英文:

Transforming a Flow<Byte> to a Flow<String> in Kotlin

问题

考虑我有一个UTF-8字节的_cold_源(例如:从磁盘上读取文件或HTTP响应的主体),以Flow&lt;Byte&gt;的形式。我如何将上述源转换为字符串的_flow_?

换句话说,我希望以下行为:

/*
 * 一个多行字符串,不以换行字符结尾。
 */
val string = &quot;&quot;&quot;
    first line
    第二行
    третья строка
&quot;&quot;&quot;.trimIndent()

assertNotEquals(&#39;\n&#39;, string.last())
assertEquals(2, string.asSequence().count { it == &#39;\n&#39; })

val source: Flow&lt;Byte&gt; = string.toByteArray().asSequence().asFlow()

val transformed: Flow&lt;String&gt; = TODO()

val target = runBlocking {
    transformed.toList(mutableListOf()).toTypedArray()
}

assertArrayEquals(
    arrayOf(&quot;first line&quot;, &quot;第二行&quot;, &quot;третья строка&quot;),
    target
)

作为额外限制,这是一个Kotlin/JS项目,因此无法使用java.io API。

英文:

Consider I have a cold source of UTF-8 bytes (e. g.: reading a file on disk, or the body of an HTTP response), in a form of a Flow&lt;Byte&gt;. How do I convert the above source to a flow of strings?

In other words, I want the following behaviour:

		/*
		 * A multi-line string, not terminated with a newline character.
		 */
		val string = &quot;&quot;&quot;
			first line
			第二行
			третья строка
		&quot;&quot;&quot;.trimIndent()

		assertNotEquals(&#39;\n&#39;, string.last())
		assertEquals(2, string.asSequence().count { it == &#39;\n&#39; })

		val source: Flow&lt;Byte&gt; = string.toByteArray().asSequence().asFlow()

		val transformed: Flow&lt;String&gt; = TODO()

		val target = runBlocking {
			transformed.toList(mutableListOf()).toTypedArray()
		}

		assertArrayEquals(
			arrayOf(&quot;first line&quot;, &quot;第二行&quot;, &quot;третья строка&quot;),
			target
		)

As an extra restriction, this is a Kotlin/JS project, so java.io APIs can't be used.

答案1

得分: 1

以下是翻译好的代码部分:

fun Flow<Byte>.decodeToString(): Flow<String> =
    flow {
        val buffer: MutableList<Byte> = arrayListOf()

        collect { value ->
            when (value) {
                /*
                 * Ignore.
                 */
                '\r'.code.toByte() -> Unit

                '\n'.code.toByte() -> {
                    emit(buffer)
                    buffer.clear()
                }

                else -> buffer.add(value)
            }
        }

        if (buffer.isNotEmpty()) {
            emit(buffer)
        }
    }
    .map(Collection<Byte>::toByteArray)
    .map(ByteArray::decodeToString)

上面的 ArrayList<Byte> 可以替换为 _okio_ 中的 okio.Buffer_kotlinx-io_ 中的 kotlinx.io.core.BytePacketBuilder,例如:

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import okio.Buffer

fun Flow<Byte>.decodeToString(): Flow<String> =
    flow {
        val buffer = Buffer()

        collect { value ->
            when (value) {
                /*
                 * Ignore.
                 */
                '\r'.code.toByte() -> Unit

                '\n'.code.toByte() -> {
                    emit(buffer.readUtf8())
                    buffer.clear()
                }

                else -> buffer.writeByte(value.toInt())
            }
        }

        if (buffer.size > 0) {
            emit(buffer.readUtf8())
        }
    }
英文:

Eventually, I came up with the following solution:

fun Flow&lt;Byte&gt;.decodeToString(): Flow&lt;String&gt; =
	flow {
		val buffer: MutableList&lt;Byte&gt; = arrayListOf()

		collect { value -&gt;
			when (value) {
				/*
				 * Ignore.
				 */
				&#39;\r&#39;.code.toByte() -&gt; Unit

				&#39;\n&#39;.code.toByte() -&gt; {
					emit(buffer)
					buffer.clear()
				}

				else -&gt; buffer.add(value)
			}
		}

		if (buffer.isNotEmpty()) {
			emit(buffer)
		}
	}
		.map(Collection&lt;Byte&gt;::toByteArray)
		.map(ByteArray::decodeToString)

The ArrayList&lt;Byte&gt; above can be replaced with either okio.Buffer from okio or kotlinx.io.core.BytePacketBuilder from kotlinx-io, e.g.:

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import okio.Buffer

fun Flow&lt;Byte&gt;.decodeToString(): Flow&lt;String&gt; =
	flow {
		val buffer = Buffer()

		collect { value -&gt;
			when (value) {
				/*
				 * Ignore.
				 */
				&#39;\r&#39;.code.toByte() -&gt; Unit

				&#39;\n&#39;.code.toByte() -&gt; {
					emit(buffer.readUtf8())
					buffer.clear()
				}

				else -&gt; buffer.writeByte(value.toInt())
			}
		}

		if (buffer.size &gt; 0) {
			emit(buffer.readUtf8())
		}
	}

huangapple
  • 本文由 发表于 2023年2月8日 17:08:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/75383429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定