# React Native for OpenHarmony 实战:去除空格实现

今天我们用 React Native 实现一个空格处理工具,支持 6 种常用的空白字符处理方式(去除所有空格、去除首尾空格、去除多余空格等),还能实时统计空白字符的数量。
状态设计
import React, { useState, useRef, useEffect } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, ScrollView, Animated } from 'react-native';
export const SpaceRemover: React.FC = () => {
const [text, setText] = useState('');
const buttonAnims = useRef(Array(6).fill(0).map(() => new Animated.Value(1))).current;
const statsAnim = useRef(new Animated.Value(1)).current;
const pulseAnim = useRef(new Animated.Value(1)).current;
状态设计包含一个状态变量和三组动画值:
文本内容 text:存储用户输入的文本,也是所有处理操作的目标。初始为空字符串。
按钮动画数组 buttonAnims:6 个处理按钮各有一个独立的动画值,初始都是 1(正常大小)。为什么需要 6 个独立的动画值?因为用户可能快速点击不同的按钮,每个按钮的动画应该独立播放,互不干扰。如果共用一个动画值,第二次点击会打断第一次的动画,看起来很不流畅。
统计区动画 statsAnim:统计卡片的缩放动画,初始值 1。当用户点击处理按钮时,文本内容变化,统计数据也会变化,这时触发统计区的动画,提示用户"数据已更新"。
脉冲动画 pulseAnim:虽然在代码中定义了,但在当前版本中没有使用。这是为未来扩展预留的,比如可以给输入框或统计区添加呼吸动画。
Array(6).fill(0).map(() => new Animated.Value(1)) 创建 6 个独立的动画值对象。不能用 Array(6).fill(new Animated.Value(1)),因为那样会让所有元素共享同一个对象,修改一个就会影响其他的。
脉冲动画初始化
useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(pulseAnim, { toValue: 1.05, duration: 1000, useNativeDriver: true }),
Animated.timing(pulseAnim, { toValue: 1, duration: 1000, useNativeDriver: true }),
])
).start();
}, []);
组件挂载时启动脉冲动画,这是一个无限循环的呼吸动画。
动画序列:先放大到 105%(1 秒),再缩小到 100%(1 秒),总共 2 秒一个周期。Animated.sequence 让两个动画依次执行,Animated.loop 让序列无限循环。
时长选择:1 秒的时长比较舒缓,不会让用户感到烦躁。如果太快(比如 300ms),会显得很急促,像在闪烁;如果太慢(比如 3 秒),变化太慢,用户可能注意不到。
缩放幅度:1.05 表示放大 5%,这是一个很微妙的变化。太大会干扰用户阅读,太小又看不出效果。5% 是一个经过实践检验的"黄金比例"。
原生驱动:useNativeDriver: true 让动画在原生层执行,不占用 JavaScript 线程,即使主线程很忙,动画也能保持 60fps 流畅运行。
依赖数组:[] 表示这个 effect 只在组件挂载时执行一次。如果不传依赖数组,每次渲染都会重新启动动画,导致动画重复播放。如果传入 [pulseAnim],由于 pulseAnim 是 ref 不会变化,效果和 [] 一样。
虽然当前版本没有使用这个动画,但代码已经准备好了,未来只需要把 pulseAnim 应用到某个元素上就能看到效果。
按钮动画函数
const animateButton = (index: number) => {
Animated.sequence([
Animated.timing(buttonAnims[index], { toValue: 0.9, duration: 100, useNativeDriver: true }),
Animated.spring(buttonAnims[index], { toValue: 1, friction: 3, useNativeDriver: true }),
]).start();
Animated.sequence([
Animated.timing(statsAnim, { toValue: 0.95, duration: 100, useNativeDriver: true }),
Animated.spring(statsAnim, { toValue: 1, friction: 4, useNativeDriver: true }),
]).start();
};
这个函数在用户点击处理按钮时触发,同时播放两个动画:按钮动画和统计区动画。
按钮动画的工作原理:
- 第一阶段:用
Animated.timing在 100 毫秒内把按钮缩小到 90%。这个阶段模拟"按下"的动作,按钮被压缩了。 - 第二阶段:用
Animated.spring弹回到原大小(100%)。friction: 3设置摩擦力,值越小弹性越大,按钮会有明显的"回弹"效果。
为什么用弹簧动画而不是线性动画?因为弹簧动画更符合物理直觉。真实世界中,按下一个按钮后松手,按钮会弹回原位,甚至会有轻微的震动。弹簧动画模拟了这个物理过程,让交互更自然。
统计区动画的工作原理:
- 同样是先缩小(95%)再弹回(100%)
friction: 4比按钮的摩擦力大,弹性稍弱,动画更稳定
为什么统计区缩小到 95% 而按钮缩小到 90%?因为统计区比按钮大,相同的缩放比例在大元素上更明显。如果统计区也缩小到 90%,会显得太夸张,像是整个区域在"跳动"。95% 的缩放刚刚好,既能让用户注意到变化,又不会太突兀。
两个动画的关系:
- 它们同时启动(两个
start()调用没有等待) - 但作用于不同的元素(按钮 vs 统计区)
- 用户看到的效果是:点击按钮 → 按钮缩小弹回 → 统计区同时缩小弹回
这种组合动画给用户明确的反馈:“我点击了这个按钮,文本已处理,统计数据已更新”。三个信息通过一次交互传达给用户,这就是好的交互设计。
处理函数定义
const operations = [
{ name: '去除所有空格', icon: '🚫', fn: (s: string) => s.replace(/\s/g, '') },
{ name: '去除首尾空格', icon: '✂️', fn: (s: string) => s.trim() },
{ name: '去除多余空格', icon: '📏', fn: (s: string) => s.replace(/\s+/g, ' ').trim() },
{ name: '去除空行', icon: '📄', fn: (s: string) => s.split('\n').filter(line => line.trim()).join('\n') },
{ name: '每行去首尾', icon: '📋', fn: (s: string) => s.split('\n').map(line => line.trim()).join('\n') },
{ name: '去除制表符', icon: '⌨️', fn: (s: string) => s.replace(/\t/g, '') },
];
这个数组定义了 6 种空白字符处理方式,每种包含名称、图标、处理函数。这种数据驱动的设计让代码非常简洁,添加新的处理方式只需要在数组里加一项。
去除所有空格:/\s/g 匹配所有空白字符(空格、制表符、换行符等),g 表示全局匹配,replace 把它们全部替换成空字符串。比如 "Hello World\n" 变成 "HelloWorld"。
这个操作最激进,会删除所有空白字符,包括换行。适用于需要紧凑格式的场景,比如生成 URL slug、数据库查询等。但要注意,删除换行符可能会让多行文本变成一行,影响可读性。
去除首尾空格:trim() 是 JavaScript 字符串的内置方法,删除字符串开头和结尾的空白字符,但保留中间的空白。比如 " Hello World " 变成 "Hello World"。
这是最常用的操作,用于清理用户输入。用户在输入框里输入文本时,可能不小心在开头或结尾多打了空格,trim() 可以自动清理这些多余的空格。很多表单验证都会先 trim() 再检查内容。
去除多余空格:/\s+/g 匹配一个或多个连续的空白字符,replace 把它们替换成一个空格,最后用 trim() 删除首尾空格。比如 "Hello World " 变成 "Hello World"。
这个操作保留了单词之间的空格,但把多个连续空格合并成一个。适用于格式化文本,让文本看起来更整洁。从网页复制的文本经常有多余的空格,用这个操作可以快速清理。
去除空行:先用 split('\n') 按换行符分割成行数组,然后用 filter(line => line.trim()) 过滤掉空行(trim() 后为空字符串的行),最后用 join('\n') 拼回字符串。
什么是空行?就是只包含空白字符(空格、制表符)的行,或者完全空的行。line.trim() 删除行首尾空白后,如果结果是空字符串,说明这行是空行。filter 只保留 trim() 后非空的行。
比如:
Hello
World
!
变成:
Hello
World
!
这个操作常用于清理代码、文章等多行文本,删除多余的空行让内容更紧凑。
每行去首尾:和"去除空行"类似,也是先 split('\n') 分割成行,但用 map(line => line.trim()) 对每行执行 trim(),最后 join('\n') 拼回去。
这个操作保留所有行(包括空行),但删除每行首尾的空白字符。比如:
Hello
World
变成:
Hello
World
适用于清理缩进混乱的文本,或者从代码编辑器复制的文本(可能有缩进)。
去除制表符:/\t/g 匹配所有制表符,replace 把它们替换成空字符串。比如 "Hello\tWorld" 变成 "HelloWorld"。
制表符在不同编辑器中显示宽度不同(有的是 4 个空格,有的是 8 个),导致格式混乱。这个操作直接删除所有制表符,避免格式问题。如果想把制表符替换成空格,可以改成 s.replace(/\t/g, ' ')(4 个空格)。
统计指标定义
const stats = [
{ icon: '⬜', value: (text.match(/ /g) || []).length, label: '空格' },
{ icon: '⏩', value: (text.match(/\t/g) || []).length, label: '制表符' },
{ icon: '↩️', value: (text.match(/\n/g) || []).length, label: '换行符' },
];
这个数组定义了 3 个统计指标,实时显示文本中空白字符的数量。
空格统计:/ /g 匹配所有空格字符(注意正则中间是一个空格),match() 返回匹配数组,数组长度就是空格数量。如果没有匹配(match() 返回 null),用 || [] 提供默认值空数组,避免 null.length 报错。
为什么不用 /\s/g 匹配所有空白字符?因为我们要分别统计空格、制表符、换行符,如果用 \s 会把它们全部匹配,无法区分。
制表符统计:/\t/g 匹配所有制表符。\t 是制表符的转义表示,在字符串中写 "\t" 就是一个制表符。
制表符在文本中不可见(或者显示为很宽的空白),但确实存在。统计制表符的数量可以帮助用户发现隐藏的格式问题。比如代码缩进混用空格和制表符,看起来对齐了,但实际上格式不一致。
换行符统计:/\n/g 匹配所有换行符。换行符的数量通常等于行数减 1(最后一行后面没有换行符)。
为什么要统计换行符?因为有些场景对行数有限制,比如短信、推特等。统计换行符可以帮助用户了解文本的结构。
实时计算 vs 缓存:这三个统计指标每次渲染都会重新计算,会不会影响性能?实际上不会,因为:
- 正则匹配的性能很好,即使几千字的文本也是瞬间完成
- 用户输入的频率不高,每秒最多几十次
- React 的虚拟 DOM 会优化渲染,只更新变化的部分
如果真的遇到性能问题(比如处理几万字的文本),可以用 useMemo 缓存计算结果:
const stats = useMemo(() => [
{ icon: '⬜', value: (text.match(/ /g) || []).length, label: '空格' },
// ...
], [text]);
但在这个场景下,过度优化反而会增加代码复杂度。
鸿蒙 ArkTS 对比:处理函数
operations = [
{ name: '去除所有空格', icon: '🚫', fn: (s: string) => s.replace(/\s/g, '') },
{ name: '去除首尾空格', icon: '✂️', fn: (s: string) => s.trim() },
{ name: '去除多余空格', icon: '📏', fn: (s: string) => s.replace(/\s+/g, ' ').trim() },
{ name: '去除空行', icon: '📄', fn: (s: string) => s.split('\n').filter(line => line.trim()).join('\n') },
{ name: '每行去首尾', icon: '📋', fn: (s: string) => s.split('\n').map(line => line.trim()).join('\n') },
{ name: '去除制表符', icon: '⌨️', fn: (s: string) => s.replace(/\t/g, '') },
]
get stats() {
return [
{ icon: '⬜', value: (this.text.match(/ /g) || []).length, label: '空格' },
{ icon: '⏩', value: (this.text.match(/\t/g) || []).length, label: '制表符' },
{ icon: '↩️', value: (this.text.match(/\n/g) || []).length, label: '换行符' },
]
}
ArkTS 中的处理函数和统计逻辑完全一样,因为字符串方法、正则表达式、数组方法都是 JavaScript 标准 API,跨平台通用。这就是 React Native 的优势——核心业务逻辑可以直接复用,不需要为每个平台重写。
界面渲染:头部和输入框
return (
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerIcon}>✂️</Text>
<Text style={styles.headerTitle}>去除空格</Text>
<Text style={styles.headerSubtitle}>多种空格处理方式</Text>
</View>
<View style={styles.inputCard}>
<TextInput
style={styles.input}
value={text}
onChangeText={setText}
multiline
placeholder="输入要处理的文本..."
placeholderTextColor="#666"
/>
</View>
头部区域包含图标、标题、副标题,和其他工具保持一致的风格。✂️ 剪刀图标表示"剪掉"多余空格的概念,直观易懂。
输入框设计:
- 用
View包裹TextInput,形成双层结构(卡片 + 输入框) multiline让输入框支持多行输入,因为空格处理经常涉及多行文本placeholder提示用户"输入要处理的文本",明确告诉用户这个工具的用法placeholderTextColor="#666"设置占位符颜色为灰色,和实际输入的白色文本形成对比
为什么不给输入框加动画?因为输入框是用户交互的主要区域,如果加动画(比如缩放、抖动),会干扰用户输入。想象一下,你正在输入文本,输入框突然缩小又放大,光标位置可能会跳动,输入体验很差。
所以我们把动画应用在统计区和按钮上,这些区域不涉及用户输入,动画不会造成干扰。
统计区渲染
<Animated.View style={[styles.statsRow, { transform: [{ scale: statsAnim }] }]}>
{stats.map((stat, i) => (
<View key={i} style={styles.statItem}>
<Text style={styles.statIcon}>{stat.icon}</Text>
<Text style={styles.statValue}>{stat.value}</Text>
<Text style={styles.statLabel}>{stat.label}</Text>
</View>
))}
</Animated.View>
统计区用 Animated.View 包裹,应用缩放动画。当用户点击处理按钮时,文本内容变化,统计数据也会变化,这时触发动画,提示用户"数据已更新"。
横向布局:flexDirection: 'row' 让三个统计项横向排列,flex: 1 让它们平均分配宽度。这样无论屏幕多宽,三个统计项总是等宽的。
统计项内容:
- 顶部是 emoji 图标(⬜ 空格、⏩ 制表符、↩️ 换行符),24 号字体
- 中间是数值,24 号粗体,蓝色(
#4A90D9),是视觉焦点 - 底部是标签,12 号灰色文字,说明这个数值的含义
为什么用 emoji 图标?因为 emoji 是跨平台的,不需要导入图片资源,也不需要字体文件。而且 emoji 的含义很直观,用户一眼就能理解。⬜ 表示空白,⏩ 表示快进(制表符让光标快速移动),↩️ 表示回车换行。
数值颜色:用蓝色而不是白色,是为了突出显示。蓝色在深色背景上很醒目,而且给人"科技感"的印象。如果用白色,数值会和标签混在一起,不够突出。
处理按钮渲染
<View style={styles.buttons}>
{operations.map(({ name, icon, fn }, index) => (
<Animated.View
key={name}
style={[styles.btnWrapper, { transform: [{ scale: buttonAnims[index] }] }]}
>
<TouchableOpacity
style={styles.btn}
onPress={() => { animateButton(index); setText(fn(text)); }}
activeOpacity={0.8}
>
<Text style={styles.btnIcon}>{icon}</Text>
<Text style={styles.btnText}>{name}</Text>
</TouchableOpacity>
</Animated.View>
))}
</View>
</ScrollView>
);
};
按钮区域用网格布局,每行两个按钮。map 遍历 operations 数组,为每种处理方式生成一个按钮。
解构赋值:{ name, icon, fn } 从数组元素中提取需要的属性,index 是数组索引,用于访问对应的动画值。这种写法让代码更简洁,不需要写 operations[index].name。
动画包裹:每个按钮用 Animated.View 包裹,应用独立的缩放动画。key={name} 是 React 列表渲染的要求,用唯一的 key 标识每个元素。为什么用 name 而不是 index?因为 name 是唯一的,而且不会变化。如果用 index,当数组顺序改变时,React 可能会错误地复用组件。
点击处理:onPress 做了两件事:
animateButton(index):触发按钮和统计区的动画setText(fn(text)):调用处理函数,把结果更新到状态
这两个操作的顺序很重要。先触发动画,再更新文本,这样动画和文本更新几乎同时发生,用户感觉很流畅。如果先更新文本再触发动画,可能会有轻微的延迟感。
按钮内容:图标和文字横向排列,图标在左,文字在右。marginRight: 8 在它们之间留出间距,避免粘在一起。
触摸反馈:activeOpacity={0.8} 让按钮按下时稍微变暗,配合缩放动画,给用户双重反馈。视觉上,按钮变暗了;动画上,按钮缩小了。这种多重反馈让用户确信"我的操作已经生效"。
鸿蒙 ArkTS 对比:界面渲染
Column() {
// 头部
Column() {
Text('✂️').fontSize(50).margin({ bottom: 8 })
Text('去除空格').fontSize(28).fontWeight(FontWeight.Bold).fontColor(Color.White)
Text('多种空格处理方式').fontSize(14).fontColor('#888888').margin({ top: 4 })
}
.width('100%')
.alignItems(HorizontalAlign.Center)
.margin({ bottom: 24 })
// 输入框
TextArea({ text: this.text, placeholder: '输入要处理的文本...' })
.width('100%')
.height(150)
.backgroundColor('#252550')
.borderRadius(16)
.onChange((value: string) => {
this.text = value
})
// 统计区
Row() {
ForEach(this.stats, (stat: StatItem) => {
Column() {
Text(stat.icon).fontSize(24).margin({ bottom: 4 })
Text(stat.value.toString()).fontSize(24).fontWeight(FontWeight.Bold).fontColor('#4A90D9')
Text(stat.label).fontSize(12).fontColor('#888888').margin({ top: 4 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Center)
})
}
.width('100%')
.backgroundColor('#1a1a3e')
.borderRadius(16)
.padding(16)
.scale({ x: this.statsScale, y: this.statsScale })
// 按钮
Grid() {
ForEach(this.operations, (op: Operation, index: number) => {
GridItem() {
Button() {
Row() {
Text(op.icon).fontSize(18).margin({ right: 8 })
Text(op.name).fontSize(13).fontWeight(FontWeight.Medium)
}
}
.width('100%')
.backgroundColor('#1a1a3e')
.onClick(() => {
this.animateButton(index)
this.text = op.fn(this.text)
})
.scale({ x: this.buttonScales[index], y: this.buttonScales[index] })
}
})
}
.columnsTemplate('1fr 1fr')
.rowsGap(8)
.columnsGap(8)
}
鸿蒙的布局结构和 React Native 类似,只是语法不同。Column 对应 View(垂直布局),Row 对应 View(水平布局),TextArea 对应 TextInput,Grid 对应网格布局。
核心逻辑(处理函数、统计计算)完全一样,只是界面代码需要适配鸿蒙的组件和 API。
样式定义:容器和头部
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#0f0f23', padding: 20 },
header: { alignItems: 'center', marginBottom: 24 },
headerIcon: { fontSize: 50, marginBottom: 8 },
headerTitle: { fontSize: 28, fontWeight: '700', color: '#fff' },
headerSubtitle: { fontSize: 14, color: '#888', marginTop: 4 },
容器用深蓝色背景 #0f0f23,这是整个应用的统一配色。padding: 20 在四周留出边距,让内容不会紧贴屏幕边缘,看起来更舒适。
头部元素全部居中对齐(alignItems: 'center'),图标、标题、副标题垂直排列。字号从大到小(50 → 28 → 14),颜色从亮到暗(emoji → 白色 → 灰色),形成清晰的视觉层次,引导用户的视线从上到下阅读。
marginBottom: 24 在头部和输入框之间留出间距,避免元素挤在一起。24 像素是一个常用的间距值,不会太大也不会太小。
样式定义:输入框
inputCard: {
backgroundColor: '#1a1a3e',
borderRadius: 20,
padding: 4,
marginBottom: 20,
borderWidth: 1,
borderColor: '#3a3a6a',
},
input: {
backgroundColor: '#252550',
padding: 16,
borderRadius: 16,
minHeight: 150,
fontSize: 16,
color: '#fff',
},
输入框用双层结构:外层是卡片容器(inputCard),内层是实际的输入框(input)。
卡片容器的设计:
- 背景色
#1a1a3e比容器背景稍亮,形成层次感 - 圆角 20 让卡片看起来柔和,不会太生硬
- 内边距 4 在卡片和输入框之间留出一圈间隙,形成"内嵌"效果
- 边框颜色
#3a3a6a比背景更亮,让卡片有立体感,像是"浮"在背景上
输入框的设计:
- 背景色
#252550比卡片背景更深,和卡片形成对比,让用户知道"这是可以输入的区域" - 圆角 16 比卡片的圆角 20 小,这样四个角不会超出卡片
- 最小高度 150 像素,确保输入框有足够的空间显示多行文本
- 字号 16,大小适中,既不会太小看不清,也不会太大占空间
- 白色文字(
color: '#fff')在深色背景上很醒目
为什么要用双层结构?因为这样可以在卡片和输入框之间留出间隙,形成"内嵌"效果。如果直接给输入框加边框,效果不如双层结构好看。这是一个常见的 UI 设计技巧。
样式定义:统计区
statsRow: {
flexDirection: 'row',
backgroundColor: '#1a1a3e',
borderRadius: 16,
padding: 16,
marginBottom: 20,
borderWidth: 1,
borderColor: '#3a3a6a',
},
statItem: { flex: 1, alignItems: 'center' },
statIcon: { fontSize: 24, marginBottom: 4 },
statValue: { fontSize: 24, fontWeight: '700', color: '#4A90D9' },
statLabel: { fontSize: 12, color: '#888', marginTop: 4 },
统计区是一个横向布局的卡片,包含三个统计项。
统计区容器:
flexDirection: 'row'让三个统计项横向排列- 背景色、圆角、边框和输入框卡片一致,保持视觉统一
- 内边距 16 让内容不会紧贴边缘
统计项布局:
flex: 1让三个统计项平均分配宽度,无论屏幕多宽,它们总是等宽的alignItems: 'center'让图标、数值、标签垂直居中对齐
统计项内容:
- 图标 24 号字体,比较醒目
- 数值 24 号粗体,蓝色(
#4A90D9),是视觉焦点。为什么用蓝色?因为蓝色在深色背景上很醒目,而且给人"科技感"的印象 - 标签 12 号灰色文字,比数值小,起辅助说明作用
图标、数值、标签之间用 marginBottom 和 marginTop 留出间距,避免挤在一起。4 像素的间距很小,但足以让元素分开。
样式定义:按钮
buttons: { flexDirection: 'row', flexWrap: 'wrap' },
btnWrapper: { width: '48%', margin: '1%' },
btn: {
backgroundColor: '#1a1a3e',
padding: 16,
borderRadius: 12,
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'center',
borderWidth: 1,
borderColor: '#3a3a6a',
},
btnIcon: { fontSize: 18, marginRight: 8 },
btnText: { color: '#fff', fontSize: 13, fontWeight: '600' },
});
按钮区域用 Flexbox 布局,flexWrap: 'wrap' 让按钮自动换行。
按钮包裹器的尺寸计算:
- 宽度 48%,左右各留 1% 的外边距
- 两个按钮加起来:48% + 1% + 1% + 48% + 1% + 1% = 100%,正好占满一行
- 这种百分比布局可以自适应不同屏幕宽度
为什么不用固定像素宽度?因为不同设备的屏幕宽度不同。iPhone SE 的屏幕宽度是 320px,iPad 的屏幕宽度是 768px。如果用固定宽度,在小屏幕上按钮可能太大,在大屏幕上按钮可能太小。用百分比可以自适应,在任何屏幕上都能保持合适的比例。
按钮样式:
- 深蓝色背景
#1a1a3e,和卡片背景一致 - 圆角 12,比卡片的圆角 16 小,因为按钮比卡片小
- 内边距 16,让按钮有足够的点击区域。按钮太小不好点,太大又浪费空间,16 像素是一个平衡点
flexDirection: 'row'让图标和文字横向排列justifyContent: 'center'让它们水平居中alignItems: 'center'让它们垂直居中
按钮内容:
- 图标 18 号字体,文字 13 号字体,图标稍大一点更醒目
- 文字用中等粗细(
fontWeight: '600'),比正常文字粗一点,但不像标题那么粗 marginRight: 8在图标和文字之间留出间距
边框颜色和其他卡片一样,保持视觉一致性。所有卡片、按钮、输入框都用相同的配色方案(背景 #1a1a3e,边框 #3a3a6a),让界面看起来很统一。
小结
这个去除空格工具展示了文本处理的常见模式。6 种处理方式通过正则表达式和字符串方法实现,从简单的 trim() 到复杂的"每行去首尾",满足不同需求。实时统计功能让用户了解文本中空格、制表符、换行符的数量。按钮和统计区的动画让工具用起来更流畅。在 OpenHarmony 平台上,正则表达式和字符串方法是 JavaScript 标准 API,跨平台通用。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)