Supplier <Sequence <String >>不能重复多次

时间:2019-08-17 09:59:57

标签: java kotlin websocket

我想在Kotlin脚本中使用Java Web套接字API接收HTTP请求。

我在请求中只需要两行,因此我尝试将整个请求作为String行来进行处理,对其进行迭代,并为这两行中的每行获取与regex匹配的行。

我使用文本文件的FileInputStream进行了测试,而不是从客户端发送的实际InputStream进行了测试。我只能迭代一次,所以无法获得第二行。我收到以下结果。

  

GET /hello.html HTTP / 1.1

     

线程“主”中的异常java.util.NoSuchElementException:序列不包含与谓词匹配的元素。     在RequestParam $ filterLines $ 1.invoke(RequestParam.kt:29)     在RequestParam $ filterLines $ 1.invoke(RequestParam.kt:6)     在RequestParam。(RequestParam.kt:19)     在RequestParamKt.main(RequestParam.kt:26)

官方Kotlin参考文献指出,用asSequence()实例化的Sequence不能重复多次。因此,我使用了Supplier接口,但这不起作用。

我之前尝试过相同的方法,但它确实起作用。我现在正在重写代码,因为以前的版本有点混乱。以前的版本是here

下面是我正在努力的当前版本。

import java.io.FileInputStream
import java.io.InputStream
import java.util.function.Supplier
import kotlin.streams.asSequence

class RequestParam(private val inputStream: InputStream) {
    val path: String
    val host: String

    init {
        //I'd like to receive request from client as multiple lines of String.
        //I generated Supplier since instance made by asSequence() cannot be iterated more than once https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/index.html
        val linesSupplier: Supplier<Sequence<String>> = Supplier { inputStream.bufferedReader().lines().asSequence() }

        //I only need lines starting with "GET" or "Host:"
        val regex = { x: String -> { y: String -> y.matches(Regex(x)) } }
        val regexGet = regex("GET.*")
        val regexHost = regex("Host:.*")

        //Iterate supplier and get the first line that matches each regex
        val filterLines = { x: Sequence<String>, y: (String) -> Boolean -> x.first(y) }
        path = filterLines(linesSupplier.get(), regexGet)
        println(path) //works fine
        host = filterLines(linesSupplier.get(), regexHost)
        println(host) // no good
    }
}

fun main(args: Array<String>) {
    //Test with some file
    val inputStream = FileInputStream("C:\\path\\to\\my\\test.txt")
    val requestParam = RequestParam(inputStream)
}

以及我在测试中使用的文本文件

GET /hello.html HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.barfoo.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

为什么我不能多次迭代linesSupplier?

2 个答案:

答案 0 :(得分:2)

  

表示延迟评估的集合的序列类型。顶层   实例化序列的函数和扩展函数   序列。

对于没有解决方案的解决方案:如文档所述,序列应尝试懒惰地评估inputStream的“ lines”方法返回的流中的值。因此,对于第一个调用,它将读取流,而第二个调用将尝试执行相同的操作,这将失败,因为您无法两次读取该流。您可以使用toList而不是asSequence解决它。列表将读取一次并将其保存在内存中。如果您的内存不受限,它将解决您的问题。

val linesSupplier: List<String> = inputStream.bufferedReader().lines().toList()

编辑: 对于与供应商的解决方案:您应该能够通过将文件流的创建移至供应商来使其工作。在您的代码段中,供应商失败,因为您使用的是已经消耗的相同流。

val linesSupplier: Supplier<Sequence<String>> = Supplier { FileInputStream("C:\\test.txt").bufferedReader().lines().asSequence() }

答案 1 :(得分:0)

因为在第一次调用linesSupplier.get()时消耗了inputStream,而在第二次调用时它引发了异常。不用使用供应商,只需定义一个普通的Sequence并使用它即可:

val lines: Sequence<String> =  inputStream.bufferedReader().lines().asSequence()

//...

path = filterLines(lines, regexGet)

//...

host = filterLines(lines, regexHost)