KMP OpenHarmony 数据验证工具 - 验证各种数据格式

目录
概述
本文档介绍如何在 Kotlin Multiplatform (KMP) 鸿蒙跨端开发中实现一个功能完整的数据验证工具系统。数据验证是现代应用开发中的核心需求,无论是用户输入验证、数据格式检查还是业务规则验证,都需要可靠的验证机制。这个工具提供了对多种常见数据格式的验证支持,包括电子邮件、电话号码、URL、身份证号、银行卡号、IP地址、日期格式等。
在实际应用中,数据验证工具广泛应用于以下场景:用户注册和登录系统、表单提交验证、数据导入导出、API 请求验证、支付系统、身份认证等。通过 KMP 框架的跨端能力,我们可以在不同平台上使用相同的验证逻辑,确保数据验证的一致性和可靠性。
工具的特点
- 多格式支持:支持电子邮件、电话、URL、身份证、银行卡、IP地址、日期等多种格式验证
- 正则表达式:使用强大的正则表达式进行精确匹配
- 自定义规则:支持自定义验证规则和扩展
- 详细反馈:提供详细的验证错误信息和建议
- 高性能:优化的验证算法,支持批量验证
- 跨端兼容:一份 Kotlin 代码可同时服务多个平台
工具功能
1. 电子邮件验证
电子邮件验证是最常见的数据验证需求。一个有效的电子邮件地址需要符合特定的格式规范,包括用户名部分、@符号和域名部分。电子邮件验证不仅需要检查格式是否正确,还需要验证域名是否有效。在实际应用中,电子邮件验证通常是用户注册、账户恢复和通知系统的第一步。
- 格式检查:验证电子邮件地址的基本格式
- 域名验证:检查域名是否有效
- 特殊字符处理:正确处理电子邮件中允许的特殊字符
- 国际化支持:支持国际化域名和本地化电子邮件地址
2. 电话号码验证
电话号码验证需要支持多种格式和国家代码。不同国家的电话号码格式差异很大,包括不同的国家代码、区号和本地号码长度。有效的电话号码验证需要考虑这些差异,同时提供灵活的验证选项。在电商、社交媒体和通讯应用中,电话号码验证是必不可少的。
- 国际格式支持:支持多种国家的电话号码格式
- 格式转换:将不同格式的电话号码转换为标准格式
- 运营商识别:识别电话号码的运营商信息
- 有效性检查:验证电话号码是否有效
3. URL 验证
URL 验证需要检查网址的格式是否正确,包括协议、域名、路径和查询参数等。有效的 URL 验证对于网页爬虫、链接检查和网络应用都很重要。URL 验证需要考虑各种特殊情况,如带有端口号的 URL、包含查询参数的 URL 等。
- 协议验证:检查 HTTP、HTTPS 等协议
- 域名验证:验证域名的有效性
- 路径验证:检查 URL 路径的合法性
- 查询参数验证:验证 URL 查询参数的格式
4. 身份证号验证
身份证号验证是中国应用中的常见需求。中国身份证号有特定的格式和校验算法,包括地区代码、出生日期和顺序号等。有效的身份证号验证需要检查格式、日期有效性和校验位。
- 格式检查:验证身份证号的基本格式
- 日期验证:检查出生日期是否有效
- 校验位验证:使用标准算法验证校验位
- 地区代码验证:验证地区代码是否有效
5. 银行卡号验证
银行卡号验证使用 Luhn 算法进行校验。银行卡号由发卡行标识、账户标识和校验位组成。有效的银行卡号验证需要检查格式、长度和校验位。
- Luhn 算法:使用标准的 Luhn 算法进行校验
- 卡号类型识别:识别信用卡、借记卡等类型
- 发卡行识别:识别发卡银行
- 有效期验证:检查卡号是否过期
6. IP 地址验证
IP 地址验证需要支持 IPv4 和 IPv6 两种格式。IPv4 地址由四个 0-255 的数字组成,而 IPv6 地址使用十六进制表示。有效的 IP 地址验证对于网络应用和系统管理很重要。
- IPv4 验证:检查 IPv4 地址的格式
- IPv6 验证:支持 IPv6 地址验证
- 特殊地址识别:识别本地地址、广播地址等特殊 IP
- CIDR 表示法:支持 CIDR 表示法的验证
7. 日期格式验证
日期格式验证需要支持多种日期格式,包括 YYYY-MM-DD、DD/MM/YYYY 等。有效的日期验证需要检查日期的合法性,包括闰年、月份天数等。
- 多格式支持:支持多种日期格式
- 日期合法性:检查日期是否有效(如闰年)
- 日期范围验证:验证日期是否在指定范围内
- 时间戳转换:支持与时间戳的相互转换
核心实现
1. 电子邮件验证
fun validateEmail(email: String): Boolean {
val emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}$".toRegex()
return emailRegex.matches(email)
}
fun validateEmailStrict(email: String): Pair<Boolean, String> {
if (email.isEmpty()) {
return Pair(false, "电子邮件不能为空")
}
if (email.length > 254) {
return Pair(false, "电子邮件长度不能超过254个字符")
}
val emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}$".toRegex()
if (!emailRegex.matches(email)) {
return Pair(false, "电子邮件格式不正确")
}
val parts = email.split("@")
if (parts[0].length > 64) {
return Pair(false, "电子邮件用户名部分不能超过64个字符")
}
return Pair(true, "电子邮件格式正确")
}
代码说明: 电子邮件验证使用正则表达式进行基本格式检查。严格验证版本还检查长度限制和各个部分的有效性。这些检查确保了电子邮件地址的合法性。
2. 电话号码验证
fun validatePhoneNumber(phone: String, country: String = "CN"): Boolean {
val phoneRegex = when (country.uppercase()) {
"CN" -> "^1[3-9]\\d{9}$".toRegex() // 中国手机号
"US" -> "^\\+?1?\\d{10}$".toRegex() // 美国电话号
"UK" -> "^\\+?44\\d{10}$".toRegex() // 英国电话号
else -> "^\\d{7,15}$".toRegex() // 通用格式
}
return phoneRegex.matches(phone.replace(Regex("[\\s\\-()]"), ""))
}
fun validateChinesePhoneNumber(phone: String): Pair<Boolean, String> {
val cleanPhone = phone.replace(Regex("[\\s\\-()]"), "")
if (cleanPhone.isEmpty()) {
return Pair(false, "电话号码不能为空")
}
if (cleanPhone.length != 11) {
return Pair(false, "中国手机号必须是11位数字")
}
if (!cleanPhone.startsWith("1")) {
return Pair(false, "中国手机号必须以1开头")
}
val secondDigit = cleanPhone[1].toString().toInt()
if (secondDigit !in 3..9) {
return Pair(false, "中国手机号第二位必须是3-9之间的数字")
}
if (!cleanPhone.matches(Regex("^\\d+$"))) {
return Pair(false, "电话号码只能包含数字")
}
return Pair(true, "电话号码格式正确")
}
代码说明: 电话号码验证支持多个国家的格式。中国电话号码验证检查长度、首位数字和第二位数字的有效范围。这些检查确保了电话号码的有效性。
3. URL 验证
fun validateURL(url: String): Boolean {
val urlRegex = "^(https?|ftp)://[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=]+$".toRegex()
return urlRegex.matches(url)
}
fun validateURLStrict(url: String): Pair<Boolean, String> {
if (url.isEmpty()) {
return Pair(false, "URL不能为空")
}
if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("ftp://")) {
return Pair(false, "URL必须以http://、https://或ftp://开头")
}
val urlRegex = "^(https?|ftp)://[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=]+$".toRegex()
if (!urlRegex.matches(url)) {
return Pair(false, "URL格式不正确")
}
try {
val uri = java.net.URI(url)
if (uri.host == null) {
return Pair(false, "URL缺少主机名")
}
} catch (e: Exception) {
return Pair(false, "URL解析失败: ${e.message}")
}
return Pair(true, "URL格式正确")
}
代码说明: URL 验证检查协议、格式和主机名的有效性。严格验证版本使用 Java 的 URI 类进行额外的解析检查。
4. 身份证号验证
fun validateChineseIDCard(idCard: String): Pair<Boolean, String> {
val cleanID = idCard.replace(Regex("[\\s\\-]"), "")
if (cleanID.isEmpty()) {
return Pair(false, "身份证号不能为空")
}
if (cleanID.length != 18) {
return Pair(false, "身份证号必须是18位")
}
if (!cleanID.matches(Regex("^\\d{17}[\\dXx]$"))) {
return Pair(false, "身份证号格式不正确")
}
// 验证日期
val year = cleanID.substring(6, 10).toInt()
val month = cleanID.substring(10, 12).toInt()
val day = cleanID.substring(12, 14).toInt()
if (month < 1 || month > 12) {
return Pair(false, "身份证号中的月份无效")
}
if (day < 1 || day > 31) {
return Pair(false, "身份证号中的日期无效")
}
// 验证校验位
val weights = intArrayOf(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
val checkCodes = charArrayOf('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2')
var sum = 0
for (i in 0 until 17) {
sum += cleanID[i].toString().toInt() * weights[i]
}
val checkCode = checkCodes[sum % 11]
if (cleanID[17].uppercaseChar() != checkCode) {
return Pair(false, "身份证号校验位不正确")
}
return Pair(true, "身份证号有效")
}
代码说明: 身份证号验证检查长度、格式、日期有效性和校验位。使用标准的身份证号校验算法确保了验证的准确性。
5. 银行卡号验证
fun validateBankCardNumber(cardNumber: String): Pair<Boolean, String> {
val cleanCard = cardNumber.replace(Regex("[\\s\\-]"), "")
if (cleanCard.isEmpty()) {
return Pair(false, "银行卡号不能为空")
}
if (!cleanCard.matches(Regex("^\\d{13,19}$"))) {
return Pair(false, "银行卡号必须是13-19位数字")
}
// Luhn 算法验证
var sum = 0
var isEven = false
for (i in cleanCard.length - 1 downTo 0) {
var digit = cleanCard[i].toString().toInt()
if (isEven) {
digit *= 2
if (digit > 9) {
digit -= 9
}
}
sum += digit
isEven = !isEven
}
if (sum % 10 != 0) {
return Pair(false, "银行卡号校验失败")
}
// 识别卡类型
val cardType = when {
cleanCard.startsWith("4") -> "Visa"
cleanCard.startsWith("5") -> "MasterCard"
cleanCard.startsWith("3") -> "American Express"
cleanCard.startsWith("6") -> "Discover"
else -> "Unknown"
}
return Pair(true, "银行卡号有效 ($cardType)")
}
代码说明: 银行卡号验证使用 Luhn 算法进行校验。该算法是国际标准,用于验证各种卡号的有效性。同时识别卡的类型。
6. IP 地址验证
fun validateIPv4(ip: String): Boolean {
val ipRegex = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$".toRegex()
return ipRegex.matches(ip)
}
fun validateIPv6(ip: String): Boolean {
val ipv6Regex = "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$".toRegex()
return ipv6Regex.matches(ip)
}
fun validateIPAddress(ip: String): Pair<Boolean, String> {
if (ip.isEmpty()) {
return Pair(false, "IP地址不能为空")
}
return when {
validateIPv4(ip) -> Pair(true, "有效的IPv4地址")
validateIPv6(ip) -> Pair(true, "有效的IPv6地址")
else -> Pair(false, "IP地址格式不正确")
}
}
代码说明: IP 地址验证支持 IPv4 和 IPv6 两种格式。IPv4 验证检查每个八位组是否在 0-255 范围内。IPv6 验证使用正则表达式检查十六进制格式。
7. 日期格式验证
fun validateDate(dateString: String, format: String = "yyyy-MM-dd"): Pair<Boolean, String> {
return try {
val sdf = java.text.SimpleDateFormat(format)
sdf.isLenient = false
sdf.parse(dateString)
Pair(true, "日期格式正确")
} catch (e: Exception) {
Pair(false, "日期格式不正确: ${e.message}")
}
}
fun validateDateRange(dateString: String, startDate: String, endDate: String, format: String = "yyyy-MM-dd"): Pair<Boolean, String> {
return try {
val sdf = java.text.SimpleDateFormat(format)
sdf.isLenient = false
val date = sdf.parse(dateString)
val start = sdf.parse(startDate)
val end = sdf.parse(endDate)
when {
date.before(start) -> Pair(false, "日期早于起始日期")
date.after(end) -> Pair(false, "日期晚于结束日期")
else -> Pair(true, "日期在有效范围内")
}
} catch (e: Exception) {
Pair(false, "日期验证失败: ${e.message}")
}
}
代码说明: 日期验证支持多种格式和范围检查。使用 SimpleDateFormat 进行严格的日期解析,确保日期的有效性。
Kotlin 源代码
// DataValidator.kt
class DataValidator {
// 电子邮件验证
fun validateEmail(email: String): Pair<Boolean, String> {
if (email.isEmpty()) {
return Pair(false, "电子邮件不能为空")
}
if (email.length > 254) {
return Pair(false, "电子邮件长度不能超过254个字符")
}
val emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}$".toRegex()
if (!emailRegex.matches(email)) {
return Pair(false, "电子邮件格式不正确")
}
val parts = email.split("@")
if (parts[0].length > 64) {
return Pair(false, "电子邮件用户名部分不能超过64个字符")
}
return Pair(true, "电子邮件格式正确")
}
// 电话号码验证
fun validatePhoneNumber(phone: String, country: String = "CN"): Pair<Boolean, String> {
val cleanPhone = phone.replace(Regex("[\\s\\-()]"), "")
if (cleanPhone.isEmpty()) {
return Pair(false, "电话号码不能为空")
}
return when (country.uppercase()) {
"CN" -> validateChinesePhone(cleanPhone)
"US" -> validateUSPhone(cleanPhone)
else -> Pair(true, "电话号码格式接受")
}
}
private fun validateChinesePhone(phone: String): Pair<Boolean, String> {
if (phone.length != 11) {
return Pair(false, "中国手机号必须是11位数字")
}
if (!phone.startsWith("1")) {
return Pair(false, "中国手机号必须以1开头")
}
if (!phone.matches(Regex("^\\d+$"))) {
return Pair(false, "电话号码只能包含数字")
}
return Pair(true, "电话号码格式正确")
}
private fun validateUSPhone(phone: String): Pair<Boolean, String> {
if (phone.length < 10) {
return Pair(false, "美国电话号码至少需要10位数字")
}
if (!phone.matches(Regex("^\\d+$"))) {
return Pair(false, "电话号码只能包含数字")
}
return Pair(true, "电话号码格式正确")
}
// URL 验证
fun validateURL(url: String): Pair<Boolean, String> {
if (url.isEmpty()) {
return Pair(false, "URL不能为空")
}
if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("ftp://")) {
return Pair(false, "URL必须以http://、https://或ftp://开头")
}
val urlRegex = "^(https?|ftp)://[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=]+$".toRegex()
if (!urlRegex.matches(url)) {
return Pair(false, "URL格式不正确")
}
return Pair(true, "URL格式正确")
}
// 身份证号验证
fun validateChineseIDCard(idCard: String): Pair<Boolean, String> {
val cleanID = idCard.replace(Regex("[\\s\\-]"), "")
if (cleanID.isEmpty()) {
return Pair(false, "身份证号不能为空")
}
if (cleanID.length != 18) {
return Pair(false, "身份证号必须是18位")
}
if (!cleanID.matches(Regex("^\\d{17}[\\dXx]$"))) {
return Pair(false, "身份证号格式不正确")
}
val weights = intArrayOf(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
val checkCodes = charArrayOf('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2')
var sum = 0
for (i in 0 until 17) {
sum += cleanID[i].toString().toInt() * weights[i]
}
val checkCode = checkCodes[sum % 11]
if (cleanID[17].uppercaseChar() != checkCode) {
return Pair(false, "身份证号校验位不正确")
}
return Pair(true, "身份证号有效")
}
// 银行卡号验证
fun validateBankCardNumber(cardNumber: String): Pair<Boolean, String> {
val cleanCard = cardNumber.replace(Regex("[\\s\\-]"), "")
if (cleanCard.isEmpty()) {
return Pair(false, "银行卡号不能为空")
}
if (!cleanCard.matches(Regex("^\\d{13,19}$"))) {
return Pair(false, "银行卡号必须是13-19位数字")
}
var sum = 0
var isEven = false
for (i in cleanCard.length - 1 downTo 0) {
var digit = cleanCard[i].toString().toInt()
if (isEven) {
digit *= 2
if (digit > 9) {
digit -= 9
}
}
sum += digit
isEven = !isEven
}
if (sum % 10 != 0) {
return Pair(false, "银行卡号校验失败")
}
val cardType = when {
cleanCard.startsWith("4") -> "Visa"
cleanCard.startsWith("5") -> "MasterCard"
cleanCard.startsWith("3") -> "American Express"
cleanCard.startsWith("6") -> "Discover"
else -> "Unknown"
}
return Pair(true, "银行卡号有效 ($cardType)")
}
// IP 地址验证
fun validateIPAddress(ip: String): Pair<Boolean, String> {
if (ip.isEmpty()) {
return Pair(false, "IP地址不能为空")
}
val ipv4Regex = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$".toRegex()
val ipv6Regex = "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$".toRegex()
return when {
ipv4Regex.matches(ip) -> Pair(true, "有效的IPv4地址")
ipv6Regex.matches(ip) -> Pair(true, "有效的IPv6地址")
else -> Pair(false, "IP地址格式不正确")
}
}
// 日期格式验证
fun validateDate(dateString: String, format: String = "yyyy-MM-dd"): Pair<Boolean, String> {
return try {
val sdf = java.text.SimpleDateFormat(format)
sdf.isLenient = false
sdf.parse(dateString)
Pair(true, "日期格式正确")
} catch (e: Exception) {
Pair(false, "日期格式不正确: ${e.message}")
}
}
// 批量验证
fun validateMultiple(data: Map<String, Pair<String, String>>): Map<String, Pair<Boolean, String>> {
val results = mutableMapOf<String, Pair<Boolean, String>>()
for ((key, value) in data) {
val (input, type) = value
results[key] = when (type.lowercase()) {
"email" -> validateEmail(input)
"phone" -> validatePhoneNumber(input)
"url" -> validateURL(input)
"idcard" -> validateChineseIDCard(input)
"bankcard" -> validateBankCardNumber(input)
"ip" -> validateIPAddress(input)
"date" -> validateDate(input)
else -> Pair(false, "未知的验证类型")
}
}
return results
}
}
fun main() {
val validator = DataValidator()
println("=== 数据验证工具演示 ===\n")
// 电子邮件验证
val emailResult = validator.validateEmail("user@example.com")
println("电子邮件验证: ${emailResult.first} - ${emailResult.second}")
// 电话号码验证
val phoneResult = validator.validatePhoneNumber("13800138000", "CN")
println("电话号码验证: ${phoneResult.first} - ${phoneResult.second}")
// URL 验证
val urlResult = validator.validateURL("https://www.example.com")
println("URL验证: ${urlResult.first} - ${urlResult.second}")
// 身份证号验证
val idResult = validator.validateChineseIDCard("110101199003071234")
println("身份证号验证: ${idResult.first} - ${idResult.second}")
// 银行卡号验证
val cardResult = validator.validateBankCardNumber("4532015112830366")
println("银行卡号验证: ${cardResult.first} - ${cardResult.second}")
// IP 地址验证
val ipResult = validator.validateIPAddress("192.168.1.1")
println("IP地址验证: ${ipResult.first} - ${ipResult.second}")
// 日期验证
val dateResult = validator.validateDate("2024-01-15")
println("日期验证: ${dateResult.first} - ${dateResult.second}")
}
Kotlin 代码说明: 这个实现提供了完整的数据验证功能。DataValidator 类包含了各种数据格式的验证方法。每个方法都返回一个 Pair<Boolean, String>,其中布尔值表示验证结果,字符串表示详细的验证消息。通过这种设计,用户可以获得清晰的验证反馈。
JavaScript 编译代码
// DataValidator.js
class DataValidator {
validateEmail(email) {
if (!email) {
return { valid: false, message: "电子邮件不能为空" };
}
if (email.length > 254) {
return { valid: false, message: "电子邮件长度不能超过254个字符" };
}
const emailRegex = /^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
if (!emailRegex.test(email)) {
return { valid: false, message: "电子邮件格式不正确" };
}
const parts = email.split("@");
if (parts[0].length > 64) {
return { valid: false, message: "电子邮件用户名部分不能超过64个字符" };
}
return { valid: true, message: "电子邮件格式正确" };
}
validatePhoneNumber(phone, country = "CN") {
const cleanPhone = phone.replace(/[\s\-()]/g, "");
if (!cleanPhone) {
return { valid: false, message: "电话号码不能为空" };
}
if (country.toUpperCase() === "CN") {
return this.validateChinesePhone(cleanPhone);
} else if (country.toUpperCase() === "US") {
return this.validateUSPhone(cleanPhone);
}
return { valid: true, message: "电话号码格式接受" };
}
validateChinesePhone(phone) {
if (phone.length !== 11) {
return { valid: false, message: "中国手机号必须是11位数字" };
}
if (!phone.startsWith("1")) {
return { valid: false, message: "中国手机号必须以1开头" };
}
if (!/^\d+$/.test(phone)) {
return { valid: false, message: "电话号码只能包含数字" };
}
return { valid: true, message: "电话号码格式正确" };
}
validateUSPhone(phone) {
if (phone.length < 10) {
return { valid: false, message: "美国电话号码至少需要10位数字" };
}
if (!/^\d+$/.test(phone)) {
return { valid: false, message: "电话号码只能包含数字" };
}
return { valid: true, message: "电话号码格式正确" };
}
validateURL(url) {
if (!url) {
return { valid: false, message: "URL不能为空" };
}
if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("ftp://")) {
return { valid: false, message: "URL必须以http://、https://或ftp://开头" };
}
const urlRegex = /^(https?|ftp):\/\/[a-zA-Z0-9\-._~:\/?#\[\]@!$&'()*+,;=]+$/;
if (!urlRegex.test(url)) {
return { valid: false, message: "URL格式不正确" };
}
return { valid: true, message: "URL格式正确" };
}
validateChineseIDCard(idCard) {
const cleanID = idCard.replace(/[\s\-]/g, "");
if (!cleanID) {
return { valid: false, message: "身份证号不能为空" };
}
if (cleanID.length !== 18) {
return { valid: false, message: "身份证号必须是18位" };
}
if (!/^\d{17}[\dXx]$/.test(cleanID)) {
return { valid: false, message: "身份证号格式不正确" };
}
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
let sum = 0;
for (let i = 0; i < 17; i++) {
sum += parseInt(cleanID[i]) * weights[i];
}
const checkCode = checkCodes[sum % 11];
if (cleanID[17].toUpperCase() !== checkCode) {
return { valid: false, message: "身份证号校验位不正确" };
}
return { valid: true, message: "身份证号有效" };
}
validateBankCardNumber(cardNumber) {
const cleanCard = cardNumber.replace(/[\s\-]/g, "");
if (!cleanCard) {
return { valid: false, message: "银行卡号不能为空" };
}
if (!/^\d{13,19}$/.test(cleanCard)) {
return { valid: false, message: "银行卡号必须是13-19位数字" };
}
let sum = 0;
let isEven = false;
for (let i = cleanCard.length - 1; i >= 0; i--) {
let digit = parseInt(cleanCard[i]);
if (isEven) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
if (sum % 10 !== 0) {
return { valid: false, message: "银行卡号校验失败" };
}
let cardType = "Unknown";
if (cleanCard.startsWith("4")) cardType = "Visa";
else if (cleanCard.startsWith("5")) cardType = "MasterCard";
else if (cleanCard.startsWith("3")) cardType = "American Express";
else if (cleanCard.startsWith("6")) cardType = "Discover";
return { valid: true, message: `银行卡号有效 (${cardType})` };
}
validateIPAddress(ip) {
if (!ip) {
return { valid: false, message: "IP地址不能为空" };
}
const ipv4Regex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$/;
if (ipv4Regex.test(ip)) {
return { valid: true, message: "有效的IPv4地址" };
} else if (ipv6Regex.test(ip)) {
return { valid: true, message: "有效的IPv6地址" };
}
return { valid: false, message: "IP地址格式不正确" };
}
validateDate(dateString, format = "yyyy-MM-dd") {
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
return { valid: false, message: "日期格式不正确" };
}
return { valid: true, message: "日期格式正确" };
} catch (e) {
return { valid: false, message: `日期验证失败: ${e.message}` };
}
}
validateMultiple(data) {
const results = {};
for (const [key, value] of Object.entries(data)) {
const [input, type] = value;
switch (type.toLowerCase()) {
case "email":
results[key] = this.validateEmail(input);
break;
case "phone":
results[key] = this.validatePhoneNumber(input);
break;
case "url":
results[key] = this.validateURL(input);
break;
case "idcard":
results[key] = this.validateChineseIDCard(input);
break;
case "bankcard":
results[key] = this.validateBankCardNumber(input);
break;
case "ip":
results[key] = this.validateIPAddress(input);
break;
case "date":
results[key] = this.validateDate(input);
break;
default:
results[key] = { valid: false, message: "未知的验证类型" };
}
}
return results;
}
}
// 使用示例
const validator = new DataValidator();
console.log("=== 数据验证工具演示 ===\n");
const emailResult = validator.validateEmail("user@example.com");
console.log(`电子邮件验证: ${emailResult.valid} - ${emailResult.message}`);
const phoneResult = validator.validatePhoneNumber("13800138000", "CN");
console.log(`电话号码验证: ${phoneResult.valid} - ${phoneResult.message}`);
const urlResult = validator.validateURL("https://www.example.com");
console.log(`URL验证: ${urlResult.valid} - ${urlResult.message}`);
const idResult = validator.validateChineseIDCard("110101199003071234");
console.log(`身份证号验证: ${idResult.valid} - ${idResult.message}`);
const cardResult = validator.validateBankCardNumber("4532015112830366");
console.log(`银行卡号验证: ${cardResult.valid} - ${cardResult.message}`);
const ipResult = validator.validateIPAddress("192.168.1.1");
console.log(`IP地址验证: ${ipResult.valid} - ${ipResult.message}`);
const dateResult = validator.validateDate("2024-01-15");
console.log(`日期验证: ${dateResult.valid} - ${dateResult.message}`);
JavaScript 代码说明: JavaScript 版本是 Kotlin 代码的直接转译。由于 JavaScript 和 Kotlin 在语法上有差异,我们使用对象替代 Pair,使用正则表达式的 test 方法进行匹配。整体逻辑和算法与 Kotlin 版本保持一致。
ArkTS 调用代码
// DataValidatorPage.ets
import { DataValidator } from './DataValidator';
@Entry
@Component
struct DataValidatorPage {
@State validationType: string = 'email';
@State inputValue: string = '';
@State validationResult: string = '';
@State isValid: boolean = false;
@State showResult: boolean = false;
private validator: DataValidator = new DataValidator();
private validationTypes = [
{ label: '电子邮件', value: 'email' },
{ label: '电话号码', value: 'phone' },
{ label: 'URL', value: 'url' },
{ label: '身份证号', value: 'idcard' },
{ label: '银行卡号', value: 'bankcard' },
{ label: 'IP地址', value: 'ip' },
{ label: '日期', value: 'date' }
];
performValidation() {
try {
let result;
switch (this.validationType) {
case 'email':
result = this.validator.validateEmail(this.inputValue);
break;
case 'phone':
result = this.validator.validatePhoneNumber(this.inputValue, 'CN');
break;
case 'url':
result = this.validator.validateURL(this.inputValue);
break;
case 'idcard':
result = this.validator.validateChineseIDCard(this.inputValue);
break;
case 'bankcard':
result = this.validator.validateBankCardNumber(this.inputValue);
break;
case 'ip':
result = this.validator.validateIPAddress(this.inputValue);
break;
case 'date':
result = this.validator.validateDate(this.inputValue);
break;
default:
result = { valid: false, message: '未知的验证类型' };
}
this.isValid = result.valid;
this.validationResult = result.message;
this.showResult = true;
} catch (error) {
this.validationResult = '验证失败: ' + (error instanceof Error ? error.message : String(error));
this.isValid = false;
this.showResult = true;
}
}
build() {
Column() {
// 标题
Text('数据验证工具')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
.textAlign(TextAlign.Center)
.width('100%')
// 验证类型选择
Column() {
Text('选择验证类型')
.fontSize(14)
.fontColor('#666666')
.margin({ bottom: 10 })
Row() {
Select(this.validationTypes)
.value(this.validationType)
.onSelect((index: number, value?: string) => {
this.validationType = value || 'email';
this.showResult = false;
})
.width('100%')
.height(40)
}
.width('100%')
.padding(10)
.backgroundColor('#f5f5f5')
.borderRadius(8)
.margin({ bottom: 20 })
}
.width('100%')
.padding(15)
.backgroundColor('#ffffff')
.borderRadius(10)
.border({ width: 1, color: '#eeeeee' })
.margin({ bottom: 20 })
// 输入区域
Column() {
Text('输入数据')
.fontSize(14)
.fontColor('#666666')
.margin({ bottom: 10 })
TextInput({ placeholder: `请输入${this.validationType}` })
.value(this.inputValue)
.onChange((value: string) => {
this.inputValue = value;
})
.width('100%')
.height(45)
.padding({ left: 10, right: 10 })
.border({ width: 1, color: '#cccccc', radius: 8 })
.fontSize(14)
}
.width('100%')
.padding(15)
.backgroundColor('#ffffff')
.borderRadius(10)
.border({ width: 1, color: '#eeeeee' })
.margin({ bottom: 20 })
// 验证按钮
Button('验证')
.width('100%')
.height(45)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.backgroundColor('#1B7837')
.fontColor('#ffffff')
.onClick(() => {
this.performValidation();
})
.margin({ bottom: 20 })
// 结果显示
if (this.showResult) {
Column() {
Row() {
Text(this.isValid ? '✓ 验证成功' : '✗ 验证失败')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(this.isValid ? '#4CAF50' : '#F44336')
}
.width('100%')
.padding(12)
.backgroundColor(this.isValid ? '#E8F5E9' : '#FFEBEE')
.borderRadius(6)
.margin({ bottom: 12 })
Text(this.validationResult)
.fontSize(14)
.fontColor('#333333')
.width('100%')
.padding(12)
.backgroundColor('#f9f9f9')
.borderRadius(6)
.textAlign(TextAlign.Start)
}
.width('100%')
.padding(15)
.backgroundColor('#ffffff')
.borderRadius(10)
.border({ width: 1, color: '#eeeeee' })
}
Blank()
}
.width('100%')
.height('100%')
.padding(15)
.backgroundColor('#f5f5f5')
.scrollable(ScrollDirection.Vertical)
}
}
ArkTS 代码说明: ArkTS 调用代码展示了如何在 OpenHarmony 应用中使用数据验证工具。页面包含验证类型选择、数据输入、验证按钮和结果显示等功能。通过 @State 装饰器管理状态,实现了完整的用户交互流程。
实战案例
案例1:用户注册表单验证
在用户注册时,需要验证电子邮件、电话号码和密码等多个字段。使用数据验证工具可以确保所有输入都符合要求。
fun validateRegistrationForm(email: String, phone: String, password: String): Map<String, Pair<Boolean, String>> {
val validator = DataValidator()
return mapOf(
"email" to validator.validateEmail(email),
"phone" to validator.validatePhoneNumber(phone, "CN"),
"password" to validatePassword(password)
)
}
fun validatePassword(password: String): Pair<Boolean, String> {
if (password.length < 8) {
return Pair(false, "密码长度至少8个字符")
}
if (!password.matches(Regex(".*[A-Z].*"))) {
return Pair(false, "密码必须包含大写字母")
}
if (!password.matches(Regex(".*[0-9].*"))) {
return Pair(false, "密码必须包含数字")
}
return Pair(true, "密码符合要求")
}
案例2:支付信息验证
在支付时,需要验证银行卡号、身份证号等敏感信息。
fun validatePaymentInfo(cardNumber: String, idCard: String): Map<String, Pair<Boolean, String>> {
val validator = DataValidator()
return mapOf(
"cardNumber" to validator.validateBankCardNumber(cardNumber),
"idCard" to validator.validateChineseIDCard(idCard)
)
}
案例3:数据导入验证
在导入数据时,需要验证多个字段的有效性。
fun validateImportData(records: List<Map<String, String>>): List<Map<String, Any>> {
val validator = DataValidator()
val results = mutableListOf<Map<String, Any>>()
for ((index, record) in records.withIndex()) {
val validationResults = mutableMapOf<String, Any>()
record["email"]?.let {
val result = validator.validateEmail(it)
validationResults["email"] = result
}
record["phone"]?.let {
val result = validator.validatePhoneNumber(it, "CN")
validationResults["phone"] = result
}
record["url"]?.let {
val result = validator.validateURL(it)
validationResults["url"] = result
}
results.add(mapOf(
"rowIndex" to index + 1,
"validations" to validationResults
))
}
return results
}
最佳实践
1. 验证顺序
在验证多个字段时,应该按照重要性顺序进行验证。首先验证必填字段,然后验证格式,最后验证业务规则。
2. 错误消息
提供清晰、具体的错误消息,帮助用户理解验证失败的原因。避免使用过于技术性的错误信息。
3. 性能优化
对于大量数据的验证,考虑使用异步验证或批量验证来提高性能。
4. 安全性
在验证敏感信息(如银行卡号、身份证号)时,确保不在日志中记录完整的数据。
5. 可维护性
将验证规则集中管理,便于后续的修改和维护。考虑使用配置文件来定义验证规则。
6. 测试
为验证函数编写完整的单元测试,确保验证逻辑的正确性。包括正常情况、边界情况和异常情况的测试。
总结
数据验证工具是现代应用开发中的核心组件。通过 KMP 框架,我们可以创建一个跨端的数据验证系统,在 Kotlin、JavaScript 和 ArkTS 中使用相同的验证逻辑。这不仅提高了代码的可维护性,还确保了不同平台上验证结果的一致性。
在实际应用中,合理使用数据验证工具可以提高应用的安全性、可靠性和用户体验。通过提供清晰的错误反馈和详细的验证信息,我们可以帮助用户更好地理解和纠正输入错误。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)