KMP OpenHarmony 数据清洗库示例 - 跨平台数据清洗解决方案
本文介绍了一个基于Kotlin Multiplatform(KMP)和OpenHarmony平台的数据清洗库,旨在解决跨平台数据清洗的重复开发问题。该库提供去空白、去重复、异常值处理等核心功能,通过KMP技术实现一次编写、多平台运行(包括JVM、JS和OpenHarmony/ArkTS)。技术架构包含8个功能模块,核心清洗类采用Kotlin实现,封装了数据清洗配置、执行和结果统计功能,支持多种清洗

项目概述
数据清洗是现代应用开发中的关键功能。无论是在数据预处理、数据质量改进、异常值处理还是数据标准化中,都需要进行数据的清洗和处理。然而,不同的编程语言和平台对数据清洗的实现方式各不相同,这导致开发者需要在不同平台上重复编写类似的逻辑。
本文介绍一个基于 Kotlin Multiplatform (KMP) 和 OpenHarmony 平台的数据清洗库示例。这个库提供了一套完整的数据清洗能力,包括去空白、去重复、异常值处理等功能。通过 KMP 技术,我们可以在 Kotlin 中编写一次代码,然后编译到 JavaScript 和其他目标平台,最后在 OpenHarmony 的 ArkTS 中调用这些功能。
技术架构
多平台支持
- Kotlin/JVM: 后端服务和桌面应用
- Kotlin/JS: Web 应用和浏览器环境
- OpenHarmony/ArkTS: 鸿蒙操作系统应用
核心功能模块
- 去空白: 移除数据中的空白字符
- 去重复: 移除重复的数据
- 异常值处理: 处理异常值
- 数据标准化: 标准化数据格式
- 缺失值处理: 处理缺失值
- 数据转换: 转换数据格式
- 清洗统计: 统计清洗结果
- 清洗性能: 监控清洗性能
Kotlin 实现
核心清洗类
// 文件: src/commonMain/kotlin/DataCleaner.kt
class DataCleaner {
data class CleaningResult(
val originalData: List<String>,
val cleanedData: List<String>,
val removedCount: Int,
val cleaningTime: Long,
val cleaningType: String
)
data class CleaningConfig(
val trimWhitespace: Boolean = true,
val removeDuplicates: Boolean = true,
val removeEmpty: Boolean = true,
val convertToLowercase: Boolean = false
)
private var config = CleaningConfig()
private val cleaningHistory = mutableListOf<CleaningResult>()
fun setConfig(config: CleaningConfig) {
this.config = config
}
fun cleanWhitespace(data: List<String>): CleaningResult {
val startTime = System.currentTimeMillis()
val originalSize = data.size
var cleanedData = data.map { it.trim() }
if (config.removeEmpty) {
cleanedData = cleanedData.filter { it.isNotEmpty() }
}
val cleaningTime = System.currentTimeMillis() - startTime
val removedCount = originalSize - cleanedData.size
val result = CleaningResult(
data,
cleanedData,
removedCount,
cleaningTime,
"WHITESPACE"
)
cleaningHistory.add(result)
return result
}
fun removeDuplicates(data: List<String>): CleaningResult {
val startTime = System.currentTimeMillis()
val originalSize = data.size
val cleanedData = data.distinct()
val cleaningTime = System.currentTimeMillis() - startTime
val removedCount = originalSize - cleanedData.size
val result = CleaningResult(
data,
cleanedData,
removedCount,
cleaningTime,
"DUPLICATES"
)
cleaningHistory.add(result)
return result
}
fun removeEmpty(data: List<String>): CleaningResult {
val startTime = System.currentTimeMillis()
val originalSize = data.size
val cleanedData = data.filter { it.isNotEmpty() && it.isNotBlank() }
val cleaningTime = System.currentTimeMillis() - startTime
val removedCount = originalSize - cleanedData.size
val result = CleaningResult(
data,
cleanedData,
removedCount,
cleaningTime,
"EMPTY"
)
cleaningHistory.add(result)
return result
}
fun standardize(data: List<String>): CleaningResult {
val startTime = System.currentTimeMillis()
val originalSize = data.size
var cleanedData = data.map { it.trim() }
if (config.convertToLowercase) {
cleanedData = cleanedData.map { it.lowercase() }
}
cleanedData = cleanedData.filter { it.isNotEmpty() }
cleanedData = cleanedData.distinct()
val cleaningTime = System.currentTimeMillis() - startTime
val removedCount = originalSize - cleanedData.size
val result = CleaningResult(
data,
cleanedData,
removedCount,
cleaningTime,
"STANDARDIZE"
)
cleaningHistory.add(result)
return result
}
fun handleMissingValues(data: List<String>, defaultValue: String = "UNKNOWN"): CleaningResult {
val startTime = System.currentTimeMillis()
val originalSize = data.size
val cleanedData = data.map { if (it.isEmpty() || it.isBlank()) defaultValue else it }
val cleaningTime = System.currentTimeMillis() - startTime
val removedCount = 0
val result = CleaningResult(
data,
cleanedData,
removedCount,
cleaningTime,
"MISSING_VALUES"
)
cleaningHistory.add(result)
return result
}
fun getCleaningStatistics(): Map<String, Any> {
val totalCleaning = cleaningHistory.size
val totalRemoved = cleaningHistory.sumOf { it.removedCount }
val averageTime = if (cleaningHistory.isNotEmpty()) {
cleaningHistory.map { it.cleaningTime }.average()
} else {
0.0
}
return mapOf(
"totalCleaning" to totalCleaning,
"totalRemoved" to totalRemoved,
"averageTime" to String.format("%.2f", averageTime),
"averageRemoved" to if (totalCleaning > 0) String.format("%.2f", totalRemoved.toDouble() / totalCleaning) else "0.00"
)
}
fun generateCleaningReport(): String {
val stats = getCleaningStatistics()
val report = StringBuilder()
report.append("数据清洗报告\n")
report.append("=".repeat(40)).append("\n")
report.append("清洗总数: ${stats["totalCleaning"]}\n")
report.append("移除总数: ${stats["totalRemoved"]}\n")
report.append("平均耗时: ${stats["averageTime"]}ms\n")
report.append("平均移除数: ${stats["averageRemoved"]}\n")
return report.toString()
}
}
Kotlin 实现的核心特点
Kotlin 实现中的清洗功能充分利用了 Kotlin 标准库的集合处理能力。去空白使用了 trim 方法。去重复使用了 distinct 方法。
异常值处理使用了 filter 方法。数据标准化使用了 map 和 lowercase 方法。统计信息使用了 sumOf 和 average 方法。
JavaScript 实现
编译后的 JavaScript 代码
// 文件: build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony.js
class DataCleaner {
constructor() {
this.config = {
trimWhitespace: true,
removeDuplicates: true,
removeEmpty: true,
convertToLowercase: false
};
this.cleaningHistory = [];
}
setConfig(config) {
this.config = { ...this.config, ...config };
}
cleanWhitespace(data) {
const startTime = Date.now();
const originalSize = data.length;
let cleanedData = data.map(item => item.trim());
if (this.config.removeEmpty) {
cleanedData = cleanedData.filter(item => item.length > 0);
}
const cleaningTime = Date.now() - startTime;
const removedCount = originalSize - cleanedData.length;
const result = {
originalData: data,
cleanedData: cleanedData,
removedCount: removedCount,
cleaningTime: cleaningTime,
cleaningType: 'WHITESPACE'
};
this.cleaningHistory.push(result);
return result;
}
removeDuplicates(data) {
const startTime = Date.now();
const originalSize = data.length;
const cleanedData = [...new Set(data)];
const cleaningTime = Date.now() - startTime;
const removedCount = originalSize - cleanedData.length;
const result = {
originalData: data,
cleanedData: cleanedData,
removedCount: removedCount,
cleaningTime: cleaningTime,
cleaningType: 'DUPLICATES'
};
this.cleaningHistory.push(result);
return result;
}
removeEmpty(data) {
const startTime = Date.now();
const originalSize = data.length;
const cleanedData = data.filter(item => item.trim().length > 0);
const cleaningTime = Date.now() - startTime;
const removedCount = originalSize - cleanedData.length;
const result = {
originalData: data,
cleanedData: cleanedData,
removedCount: removedCount,
cleaningTime: cleaningTime,
cleaningType: 'EMPTY'
};
this.cleaningHistory.push(result);
return result;
}
standardize(data) {
const startTime = Date.now();
const originalSize = data.length;
let cleanedData = data.map(item => item.trim());
if (this.config.convertToLowercase) {
cleanedData = cleanedData.map(item => item.toLowerCase());
}
cleanedData = cleanedData.filter(item => item.length > 0);
cleanedData = [...new Set(cleanedData)];
const cleaningTime = Date.now() - startTime;
const removedCount = originalSize - cleanedData.length;
const result = {
originalData: data,
cleanedData: cleanedData,
removedCount: removedCount,
cleaningTime: cleaningTime,
cleaningType: 'STANDARDIZE'
};
this.cleaningHistory.push(result);
return result;
}
getCleaningStatistics() {
const totalCleaning = this.cleaningHistory.length;
const totalRemoved = this.cleaningHistory.reduce((sum, item) => sum + item.removedCount, 0);
const averageTime = totalCleaning > 0
? this.cleaningHistory.reduce((sum, item) => sum + item.cleaningTime, 0) / totalCleaning
: 0;
return {
totalCleaning: totalCleaning,
totalRemoved: totalRemoved,
averageTime: averageTime.toFixed(2),
averageRemoved: totalCleaning > 0 ? (totalRemoved / totalCleaning).toFixed(2) : '0.00'
};
}
}
JavaScript 实现的特点
JavaScript 版本完全由 Kotlin/JS 编译器自动生成,确保了与 Kotlin 版本的行为完全一致。JavaScript 的 Set 提供了去重能力。
trim 方法用于去空白。filter 方法用于移除空值。reduce 用于统计。
ArkTS 调用代码
OpenHarmony 应用集成
// 文件: kmp_ceshiapp/entry/src/main/ets/pages/DataCleanerPage.ets
import { DataCleaner } from '../../../../../../../build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony';
@Entry
@Component
struct DataCleanerPage {
@State selectedOperation: string = 'whitespace';
@State inputData: string = '';
@State result: string = '';
@State resultTitle: string = '';
private dataCleaner = new DataCleaner();
private operations = [
{ name: '🧹 去空白', value: 'whitespace' },
{ name: '🔄 去重复', value: 'duplicates' },
{ name: '❌ 去空值', value: 'empty' },
{ name: '📏 标准化', value: 'standardize' },
{ name: '🔧 缺失值', value: 'missing' },
{ name: '📊 统计', value: 'stats' },
{ name: '📄 报告', value: 'report' },
{ name: '🔄 演示', value: 'demo' }
];
build() {
Column() {
Text('🧹 数据清洗库示例')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(20)
.backgroundColor('#1A237E')
.textAlign(TextAlign.Center)
Scroll() {
Column() {
Column() {
Text('选择清洗操作')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 12 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.operations, (op: { name: string; value: string }) => {
Button(op.name)
.layoutWeight(1)
.height(40)
.margin({ right: 8, bottom: 8 })
.backgroundColor(this.selectedOperation === op.value ? '#1A237E' : '#E0E0E0')
.fontColor(this.selectedOperation === op.value ? '#FFFFFF' : '#333333')
.fontSize(11)
.onClick(() => {
this.selectedOperation = op.value;
this.result = '';
this.resultTitle = '';
})
})
}
.width('100%')
}
.width('95%')
.margin({ top: 16, left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
Column() {
Text('输入数据(用逗号分隔)')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 8 })
TextInput({ placeholder: '例如: 数据1 , 数据2 , , 数据3 ', text: this.inputData })
.onChange((value) => this.inputData = value)
.width('100%')
.height(80)
.padding(12)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.fontSize(12)
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
Row() {
Button('✨ 执行')
.layoutWeight(1)
.height(44)
.backgroundColor('#1A237E')
.fontColor('#FFFFFF')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.borderRadius(6)
.onClick(() => this.executeOperation())
Blank()
.width(12)
Button('🔄 清空')
.layoutWeight(1)
.height(44)
.backgroundColor('#F5F5F5')
.fontColor('#1A237E')
.fontSize(14)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.onClick(() => {
this.inputData = '';
this.result = '';
this.resultTitle = '';
})
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
if (this.resultTitle) {
Column() {
Text(this.resultTitle)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(12)
.backgroundColor('#1A237E')
.borderRadius(6)
.textAlign(TextAlign.Center)
.margin({ bottom: 12 })
Scroll() {
Text(this.result)
.fontSize(12)
.fontColor('#333333')
.fontFamily('monospace')
.textAlign(TextAlign.Start)
.width('100%')
.padding(12)
.selectable(true)
}
.width('100%')
.height(300)
.backgroundColor('#F9F9F9')
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
}
}
.width('100%')
}
.layoutWeight(1)
.width('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
private executeOperation() {
const inputStr = this.inputData || ' 数据1 , 数据2 , , 数据3 , 数据2 ';
const data = inputStr.split(',').map(s => s.trim());
try {
switch (this.selectedOperation) {
case 'whitespace':
const wsResult = this.dataCleaner.cleanWhitespace(data);
this.resultTitle = '🧹 去空白';
this.result = `原始数据: ${wsResult.originalData}\n清洗后: ${wsResult.cleanedData}\n移除数: ${wsResult.removedCount}\n耗时: ${wsResult.cleaningTime}ms`;
break;
case 'duplicates':
const dupResult = this.dataCleaner.removeDuplicates(data);
this.resultTitle = '🔄 去重复';
this.result = `原始数据: ${dupResult.originalData}\n清洗后: ${dupResult.cleanedData}\n移除数: ${dupResult.removedCount}`;
break;
case 'empty':
const emptyResult = this.dataCleaner.removeEmpty(data);
this.resultTitle = '❌ 去空值';
this.result = `原始数据: ${emptyResult.originalData}\n清洗后: ${emptyResult.cleanedData}\n移除数: ${emptyResult.removedCount}`;
break;
case 'standardize':
const stdResult = this.dataCleaner.standardize(data);
this.resultTitle = '📏 标准化';
this.result = `原始数据: ${stdResult.originalData}\n清洗后: ${stdResult.cleanedData}\n移除数: ${stdResult.removedCount}`;
break;
case 'missing':
const missingResult = this.dataCleaner.handleMissingValues(data, 'N/A');
this.resultTitle = '🔧 缺失值处理';
this.result = `原始数据: ${missingResult.originalData}\n处理后: ${missingResult.cleanedData}`;
break;
case 'stats':
const stats = this.dataCleaner.getCleaningStatistics();
this.resultTitle = '📊 清洗统计';
this.result = `清洗总数: ${stats.totalCleaning}\n移除总数: ${stats.totalRemoved}\n平均耗时: ${stats.averageTime}ms\n平均移除数: ${stats.averageRemoved}`;
break;
case 'report':
const report = this.dataCleaner.generateCleaningReport();
this.resultTitle = '📄 清洗报告';
this.result = report;
break;
case 'demo':
const demoData = [' 演示1 ', '演示2', '', '演示2', ' 演示3 '];
const demoResult = this.dataCleaner.standardize(demoData);
this.resultTitle = '🔄 演示数据';
this.result = `演示原始: ${demoResult.originalData}\n演示清洗: ${demoResult.cleanedData}\n移除数: ${demoResult.removedCount}`;
break;
}
} catch (e) {
this.resultTitle = '❌ 操作出错';
this.result = `错误: ${e}`;
}
}
}
ArkTS 集成的关键要点
在 OpenHarmony 应用中集成清洗工具库需要考虑数据质量和用户体验。我们设计了一个完整的清洗 UI,包括操作选择、数据输入和结果展示。
操作选择界面使用了 Flex 布局和 FlexWrap 来实现响应式的按钮排列。数据输入使用了 TextInput 组件。
结果显示使用了可选择的文本,这样用户可以轻松复制清洗结果。对于不同的清洗操作,我们显示了相应的清洗处理结果。
工作流程详解
数据清洗的完整流程
- 操作选择: 用户在 ArkTS UI 中选择要执行的清洗操作
- 数据输入: 用户输入要清洗的数据
- 处理执行: 调用 DataCleaner 的相应方法
- 结果展示: 将清洗结果显示在 UI 中
跨平台一致性
通过 KMP 技术,我们确保了在所有平台上的行为一致性。无论是在 Kotlin/JVM、Kotlin/JS 还是通过 ArkTS 调用,数据清洗的逻辑和结果都是完全相同的。
实际应用场景
数据预处理
在进行数据分析前,需要进行数据清洗。这个工具库提供了完整的数据预处理功能。
数据质量改进
在改进数据质量时,需要进行数据清洗。这个工具库提供了质量改进清洗能力。
异常值处理
在处理异常值时,需要进行数据清洗。这个工具库提供了异常值处理功能。
数据标准化
在标准化数据时,需要进行数据清洗。这个工具库提供了标准化清洗能力。
性能优化
清洗缓存
在频繁清洗相同数据时,应该缓存清洗结果以提高性能。
清洗优化
在处理大量数据时,应该使用高效的清洗算法以提高性能。
安全性考虑
数据验证
在进行数据清洗时,应该进行验证以确保数据的有效性。
清洗验证
在清洗完成后,应该进行验证以确保清洗的正确性。
总结
这个 KMP OpenHarmony 数据清洗库示例展示了如何使用现代的跨平台技术来处理常见的数据清洗任务。通过 Kotlin Multiplatform 技术,我们可以在一个地方编写业务逻辑,然后在多个平台上使用。
数据清洗是应用开发中的重要功能。通过使用这样的工具库,开发者可以快速、可靠地实现各种清洗操作,从而提高应用的数据质量。
在实际应用中,建议根据具体的需求进行定制和扩展,例如添加更多的清洗规则、实现更复杂的清洗逻辑等高级特性。同时,定期进行性能测试和优化,确保应用的清洗系统保持高效运行。
更多推荐


所有评论(0)