[英]Kotlin load a collection of a class from a file?
在C ++中,我可以使用友好运算符>>重载从文件中加载类的集合:
friend std::istream& operator>>(std::istream& is_a, College& college_a) {
return is_a >> college_a.id_,
is_a.get(),
std::getline(is_a,college_a.name_);
}
std::set<College> colleges {};
std::copy(std::istream_iterator<College> {std::cin},
std::istream_iterator<College> {},
std::inserter(colleges, colleges.begin()));
来自这样的文件:
0707 Rowan Technical College
0980 University of Saskatchewan
1058 Belmont University
1072 Belmont Technical College
Kotlin将如何处理?
在Kotlin / Java中,有很多方法可以解析文件并拆分行。 下一个示例使用正则表达式将行拆分为id和大学名称:
data class College(val id: String, val name: String)
val colleges = File("input.txt").useLines { lines ->
val regex = Regex("(\\d+) (.+)")
lines.mapNotNull { line ->
regex.matchEntire(line)?.let {
College(it.groupValues[1], it.groupValues[2])
}
}.toList()
}
如果您需要从标准输入中读取内容,则将第三行替换为:
val colleges = InputStreamReader(System.`in`).useLines { lines ->
更新:隐藏详细信息
在Java / Kotlin中,没有将文本文件解析为对象的通用方法。 对于XML和Json文件,存在一些标准API,您可以为每种类型注册Serializer / Deserializer。
如果您需要对面向行的简单文本文件进行抽象,则必须自己构建。 下面的示例可能会给您一个想法:
fun <T : Any> File.parseLines(lineParser: (String) -> T?): List<T> =
useLines { it.mapNotNull(lineParser).toList() }
该函数使用parseLines
方法扩展File
类。
类College
的行解析器的实现如下所示:
val collegeLineParser: (String) -> College? = { line ->
val regex = Regex("(\\d+) (.+)")
regex.matchEntire(line)?.let {
College(it.groupValues[1], it.groupValues[2])
}
}
或者,如果您想缓存RegEx
:
val collegeLineParserCachedRegex = object : (String) -> College? {
val regex = Regex("(\\d+) (.+)")
override fun invoke(line: String): College? =
regex.matchEntire(line)?.let {
College(it.groupValues[1], it.groupValues[2])
}
}
现在,您可以在File
上调用方法parseLines
:
val colleges = File("input.txt").parseLines(collegeLineParser)
更新:解析器注册表
如果您不想为每个调用都提供lineParser
,则可以创建一个注册表:
object LineParserRegistry {
val parsers = ConcurrentHashMap<KClass<*>, (String) -> Any?>()
inline fun <reified T> register(noinline parser : (String) -> T?) {
parsers[T::class] = parser
}
inline fun <reified T> get(): (String) -> T? {
// force companion initializer
Class.forName(T::class.java.name)
return parsers[T::class] as (String) -> T??
}
}
inline fun <reified T : Any> File.parseLines(): List<T> =
useLines { it.mapNotNull(LineParserRegistry.get<T>()).toList() }
如果您想自动注册解析器,则需要使用init
方法添加一个伴随对象:
data class College(val id: String, val name: String) {
companion object {
init {
val collegeLineParser: (String) -> College? = { line ->
val regex = Regex("(\\d+) (.+)")
regex.matchEntire(line)?.let {
College(it.groupValues[1], it.groupValues[2])
}
}
LineParserRegistry.register(collegeLineParser)
}
}
}
首次使用College
类时,将调用此init
方法。 但是反射并不重要,因此我们不得不将Class.forName(T::class.java.name)
到注册表的get
方法中。 此调用将强制初始化伴随对象。
现在,您无需进行任何准备就可以调用解析器:
val colleges = File("input.txt").parseLines<College>()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.