gpt4 book ai didi

在 Scala Play20 中使用 BodyParser 解析文件并换行

转载 作者:行者123 更新时间:2023-12-03 00:38:08 25 4
gpt4 key购买 nike

请原谅这个问题的难懂,但我有一个 Web 应用程序,我想将一个可能很大的文件发送到服务器并让它解析格式。我正在使用 Play20 框架,而且我是 Scala 新手。

例如,如果我有一个 csv,我想用“,”分隔每一行,并最终为每个字段创建一个 List[List[String]]

目前,我认为最好的方法是使用 BodyParser(但我可能是错的)。我的代码看起来像这样:

Iteratee.fold[String, List[List[String]]]() {
(result, chunk) =>
result = chunk.splitByNewLine.splitByDelimiter // Psuedocode
}

我的第一个问题是,如何处理像下面这样的情况,即 block 在一行中间被分割:

Chunk 1:
1,2,3,4\n
5,6

Chunk 2:
7,8\n
9,10,11,12\n

我的第二个问题是,编写自己的 BodyParser 是解决此问题的正确方法吗?有更好的方法来解析这个文件吗?我主要担心的是,我希望允许文件非常大,这样我就可以在某个时刻刷新缓冲区,而不是将整个文件保留在内存中。

最佳答案

如果您的 csv 不包含转义换行符,那么很容易进行渐进式解析,而无需将整个文件放入内存中。 iteratee 库在 play.api.libs.iteratee.Parsing 中提供了一个方法搜索:

def search (needle: Array[Byte]): Enumeratee[Array[Byte], MatchInfo[Array[Byte]]]

这会将您的流划分为 Matched[Array[Byte]]Unmatched[Array[Byte]]

然后,您可以组合第一个带有 header 的迭代器和另一个将折叠到不匹配结果中的迭代器。这应该类似于以下代码:

// break at each match and concat unmatches and drop the last received element (the match)
val concatLine: Iteratee[Parsing.MatchInfo[Array[Byte]],String] =
( Enumeratee.breakE[Parsing.MatchInfo[Array[Byte]]](_.isMatch) ><>
Enumeratee.collect{ case Parsing.Unmatched(bytes) => new String(bytes)} &>>
Iteratee.consume() ).flatMap(r => Iteratee.head.map(_ => r))

// group chunks using the above iteratee and do simple csv parsing
val csvParser: Iteratee[Array[Byte], List[List[String]]] =
Parsing.search("\n".getBytes) ><>
Enumeratee.grouped( concatLine ) ><>
Enumeratee.map(_.split(',').toList) &>>
Iteratee.head.flatMap( header => Iteratee.getChunks.map(header.toList ++ _) )

// an example of a chunked simple csv file
val chunkedCsv: Enumerator[Array[Byte]] = Enumerator("""a,b,c
""","1,2,3","""
4,5,6
7,8,""","""9
""") &> Enumeratee.map(_.getBytes)

// get the result
val csvPromise: Promise[List[List[String]]] = chunkedCsv |>>> csvParser

// eventually returns List(List(a, b, c),List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))

当然你可以改进解析。如果您愿意,请与社区分享,我将不胜感激。

所以你的 Play2 Controller 会是这样的:

val requestCsvBodyParser = BodyParser(rh => csvParser.map(Right(_)))

// progressively parse the big uploaded csv like file
def postCsv = Action(requestCsvBodyParser){ rq: Request[List[List[String]]] =>
//do something with data
}

关于在 Scala Play20 中使用 BodyParser 解析文件并换行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11017913/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com