You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升Kotlin读取CSV/TXT文件并生成SQL的效率?

问题描述

我用Kotlin开发了一款JavaFX程序,实现了按分号分隔读取CSV与TXT文件并构建SQL查询的功能,但存在严重效率问题:处理1600行数据耗时5分钟,希望找到优化方法。

实现代码如下:

fun generatedDelimited(filePath: String, table: String = "") {

        val sourceFile = File(filePath)
        var line: String?
        var header: Array<String>? = null

        val lines: MutableList<List<String>> = ArrayList()
        try {
            BufferedReader(FileReader(sourceFile)).use { br ->
                header = br.readLine().split(";").toTypedArray();
                while (br.readLine().also { line = it } != null) {
                    val values : Array<String> = line!!.split(";").toTypedArray();
                    lines.add(Arrays.asList(*values))
                }
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }

        val joined = "INSERT INTO $table  (${header!!.joinToString(separator = ",")})\n"

        var textSelect = "${joined}SELECT * FROM ( \n"
        var selectUnion = ""
        var lineNo = 1
        for (line in lines) {
            var columnNo = 0
            var comma = ", "
            var select = "SELECT "
            var union = "UNION ALL\n"

            if (lines.size.equals(lineNo)) {
                union = ""
            }

            for (value in line) {
                if (columnNo ==  1) {
                    select = ""
                }
                if (line.size.equals(columnNo+1)) {
                    comma = " FROM DUAL \n$union"
                }
                selectUnion += "$select'$value' as ${header!![columnNo]}$comma"
                columnNo++
            }
            lineNo++
        }

        textSelect += "$selectUnion);"
        querySQL.text = textSelect

    }

生成的SQL示例:

INSERT INTO werwsf  (DATA1,DATA2,DATA3,DATA4,DATA5)
SELECT * FROM ( 
SELECT 'HOLA1' as DATA1, 'HAKA2' as DATA2, 'HAD3' as DATA3, '' as DATA4, 'ASDAD5' as DATA5 FROM DUAL 
UNION ALL
SELECT 'HOLA6' as DATA1, 'HAKA7' as DATA2, 'HAD8' as DATA3, 'FA9' as DATA4, 'ASDAD10' as DATA5 FROM DUAL 
);
优化方案

1. 替换字符串拼接为StringBuilder(核心优化点)

原代码中selectUnion += ...的字符串拼接方式,每次都会创建新字符串对象,1600行数据会产生大量临时对象,触发频繁GC,这是耗时的主要原因。改用StringBuilder可以大幅提升效率:

// 初始化时预估容量,避免频繁扩容
val selectUnion = StringBuilder(lines.size * 200) // 按每行平均200字符预估

后续所有selectUnion +=操作替换为selectUnion.append(...)。

2. 边读边处理,避免全量加载数据到内存

当前代码把所有行存入lines集合后再二次遍历处理,可改为读取一行就处理一行,减少内存占用的同时避免额外遍历:

BufferedReader(FileReader(sourceFile)).use { br ->
    header = br.readLine().split(";")
    // 直接在读取行的循环中构建SQL片段
    br.lineSequence().forEachIndexed { lineIndex, line ->
        val values = line.split(";")
        // 在这里处理当前行的SQL拼接,无需存入lines集合
    }
}

3. 简化SQL拼接逻辑,减少分支判断

原代码内层循环的多个条件判断(如columnNo == 1)可通过joinToString简化,代码更简洁高效:

// 处理单行SQL片段
val columnParts = values.mapIndexed { idx, value ->
    "'$value' as ${header!![idx]}"
}.joinToString(", ")
val unionSuffix = if (lineIndex == lines.size - 1) "" else "\nUNION ALL\n"
selectUnion.append("SELECT $columnParts FROM DUAL$unionSuffix")

4. 优化文件读取方式

使用Files.newBufferedReader替代FileReader,支持指定字符编码避免乱码,同时lineSequence()是懒加载实现,性能更优:

Files.newBufferedReader(Paths.get(filePath), StandardCharsets.UTF_8).use { br ->
    // 读取逻辑
}

5. 避免不必要的数组转换

原代码中split(";").toTypedArray()再转List的操作完全多余,直接使用split(";")返回的List即可:

// 替换原有代码
val values = line!!.split(";")
lines.add(values)

6. 异步处理避免阻塞UI线程

JavaFX主线程负责UI渲染,长时间的文件处理会导致界面卡顿,建议用Task把逻辑放到后台线程:

val task = object : Task<String>() {
    override fun call(): String {
        // 这里放文件读取和SQL构建逻辑,返回生成的SQL字符串
    }
}
task.setOnSucceeded {
    querySQL.text = task.value
}
Thread(task).start()

优化后的完整示例代码

import java.nio.file.Files
import java.nio.file.Paths
import java.nio.charset.StandardCharsets
import javafx.concurrent.Task

fun generatedDelimited(filePath: String, table: String = "") {
    val task = object : Task<String>() {
        override fun call(): String {
            var header: List<String>? = null
            val sqlBuilder = StringBuilder()

            try {
                Files.newBufferedReader(Paths.get(filePath), StandardCharsets.UTF_8).use { br ->
                    // 读取表头
                    header = br.readLine()?.split(";") ?: emptyList()
                    if (header.isNullOrEmpty()) return ""

                    // 构建INSERT前缀
                    val columns = header!!.joinToString(", ")
                    sqlBuilder.append("INSERT INTO $table ($columns)\nSELECT * FROM ( \n")

                    // 逐行读取并构建UNION片段
                    val lines = br.lineSequence().toList()
                    lines.forEachIndexed { lineIndex, line ->
                        val values = line.split(";")
                        // 校验列数,避免SQL错误
                        if (values.size != header!!.size) return@forEachIndexed

                        val columnParts = values.mapIndexed { idx, value ->
                            // 转义单引号,避免SQL语法错误
                            val escapedValue = value.replace("'", "''")
                            "'$escapedValue' as ${header!![idx]}"
                        }.joinToString(", ")

                        sqlBuilder.append("SELECT $columnParts FROM DUAL")
                        if (lineIndex != lines.size - 1) {
                            sqlBuilder.append("\nUNION ALL\n")
                        } else {
                            sqlBuilder.append("\n")
                        }
                    }

                    sqlBuilder.append(");")
                }
            } catch (e: Exception) {
                e.printStackTrace()
                return ""
            }

            return sqlBuilder.toString()
        }
    }

    task.setOnSucceeded {
        querySQL.text = task.value
    }
    task.setOnFailed {
        // 可添加错误提示逻辑
    }
    Thread(task).start()
}

额外注意事项

  • 原代码未处理值中的单引号(如O'Neil),会导致SQL语法错误,优化后的代码添加了value.replace("'", "''")进行转义。
  • 新增列数校验逻辑,避免数据行与表头列数不一致导致的SQL构建错误。

内容的提问来源于stack exchange,提问作者Edisson Gabriel López

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 15:25:14