英文:
Transforming a Flow<Byte> to a Flow<String> in Kotlin
问题
考虑我有一个UTF-8字节的_cold_源(例如:从磁盘上读取文件或HTTP响应的主体),以Flow<Byte>
的形式。我如何将上述源转换为字符串的_flow_?
换句话说,我希望以下行为:
/*
* 一个多行字符串,不以换行字符结尾。
*/
val string = """
first line
第二行
третья строка
""".trimIndent()
assertNotEquals('\n', string.last())
assertEquals(2, string.asSequence().count { it == '\n' })
val source: Flow<Byte> = string.toByteArray().asSequence().asFlow()
val transformed: Flow<String> = TODO()
val target = runBlocking {
transformed.toList(mutableListOf()).toTypedArray()
}
assertArrayEquals(
arrayOf("first line", "第二行", "третья строка"),
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<Byte>
. 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 = """
first line
第二行
третья строка
""".trimIndent()
assertNotEquals('\n', string.last())
assertEquals(2, string.asSequence().count { it == '\n' })
val source: Flow<Byte> = string.toByteArray().asSequence().asFlow()
val transformed: Flow<String> = TODO()
val target = runBlocking {
transformed.toList(mutableListOf()).toTypedArray()
}
assertArrayEquals(
arrayOf("first line", "第二行", "третья строка"),
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<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)
The ArrayList<Byte>
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<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())
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论