当前位置: 首页 > news >正文

WPS JS宏实现去掉文档中的所有空行

WPS改造系列文章:

1.在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档教程:在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档教程_wps javascript-CSDN博客 

2.在WPS中通过JavaScript宏(JSA)调用DeepSeek官网API优化文档教程:在WPS中通过JavaScript宏(JSA)调用DeepSeek官网API优化文档教程_wps js api-CSDN博客 

3.在WPS中通过JavaScript宏(JSA)调用DeepSeek官方API自动识别标题级别和目录:在WPS中通过JavaScript宏(JSA)调用DeepSeek官方API自动识别标题级别和目录_wps js宏官方文档-CSDN博客 

4.基于Deepseek对WPS文档自动设置标题格式的代码优化:基于Deepseek对WPS文档自动设置标题格式的代码优化_deepseek识别一级标题-CSDN博客 

5.基于JSA宏对WPS文档中所有图片设置为居中显示的代码实现:基于JSA宏对WPS文档中所有图片设置为居中显示的代码实现_wps调整图片宏代码-CSDN博客 

6.WPS中代码段的识别方法及JS宏实现:WPS中代码段的识别方法及JS宏实现-CSDN博客 

WPS的宏是用JavaScript编写的,如何处理文档中的空行呢?

一、实现思路

1.如何寻找空行

空行通常指的是段落之间没有内容的行。在Word文档中,段落由段落标记分隔。每个段落都有一个段落标记,所以如果段落的内容为空,或者只有空格或制表符等空白字符,那么可能被视为空行。

所以,我的思路应该是遍历文档中的所有段落,检查每个段落是否是空行,如果是的话就删除它。不过,直接删除段落可能会导致一些问题,比如循环中的索引变化。例如,如果删除第i个段落,后面的段落索引都会减一,所以可能需要从后往前遍历,这样删除前面的段落不会影响后续的索引。

不过,在JS宏中,处理Word文档的段落集合可能和VBA有所不同。需要确认JS API中的方法和属性。比如,Documents对象、ActiveDocument,Paragraphs集合等。

(1)获取活动文档:var doc = Application.ActiveDocument;

(2)获取所有段落:var paras = doc.Paragraphs;

通过以上分析,认为通过两个步骤来获取文档中的所有段落,再确定这些段落是否是空行。

2.段落集合的遍历方式确认

这个时候,又要解决新问题,JS中集合的索引可能从0开始还是从1开始?这点需要确认。在VBA中,通常是1-based,但JS可能不同?

假设paras是按集合排列的,可能需要用item(index)来访问每个段落。而遍历时,如果从前往后删除,会导致后面的段落位置变化,可能漏掉某些段落。例如,删除第1个段落后,原来的第2个段落变成第1个,但循环变量i已经递增到下一个,导致跳过。因此,正确的做法应该是从后往前遍历,这样删除当前段落不会影响前面未处理的段落索引

所以,循环应该从paras.Count - 1开始,递减到0。不过,JS的集合可能不支持这样的索引方式,或者需要确认Count属性的存在。

3.判断段落是否是空行

另外,如何判断一个段落是否为空行?段落的内容可能包含段落标记本身,所以需要获取段落的Range.Text属性,然后去除段落标记后检查是否为空。例如,在VBA中,段落的内容是包含段落标记的,所以一个空段落可能只有一个段落标记,即文本长度为1(如"\r"或"\r\n")。但在JS宏中,可能需要用类似的方法。所以,对于每个段落,获取其Range.Text,然后去掉段落末尾的换行符,再检查是否为空字符串,或者仅包含空白字符(空格、制表符等)。例如,假设段落的内容是"\r",那么去掉换行符后是空字符串,就是空行。或者,如果段落中有空格或制表符,但其他内容也没有,那么也算空行吗?用户可能希望删除所有仅包含空白字符的行,而不仅仅是完全空的行。所以,可能需要使用正则表达式来匹配段落内容中的非空白字符。例如,如果段落的内容去除换行符后,匹配^\s*$,那么就是空行。

具体步骤:

(1)从后往前遍历所有段落。

(2)对于每个段落,获取其文本内容。

(3)去除末尾的段落标记(比如换行符)。

(4)检查剩余内容是否为空或仅包含空白字符。

(5)如果是,则删除该段落。

4.如何删除空行段落

如何删除段落?在Word对象模型中,Paragraph.Range.Delete方法可以删除该段落的范围。在JS宏中,可能需要调用Range.Delete()方法。

确认一下删除空行段落的代码结构:

function removeEmptyLines() {var doc = Application.ActiveDocument;var paras = doc.Paragraphs;var i;for (i = paras.Count - 1; i >= 0; i--) {var para = paras.Item(i);var text = para.Range.Text;// 去除段落末尾的换行符,比如用正则替换掉var content = text.replace(/[\r\n]/g, '');// 检查是否为空或仅空白字符if (/^\s*$/.test(content)) {para.Range.Delete();}}}

5.确定段落总数

但需要注意,在JS中,集合的Item索引是否从0开始?比如在VBA中,Item是1-based的,所以如果JS中是0-based,那么循环应该从paras.Count -1开始,到0。或者可能WPS JS宏的API和VBA类似,Item索引是1-based的,这样paras.Count是段落总数,Item(i)中的i从1到 paras.Count。这时候如果从后往前遍历,应该i从 paras.Count 到 1,每次减1。这可能导致代码中的错误。比如,在VBA中,通常用For i = paras.Count To 1 Step -1。但在JS中,如果Item是0-based,那么应该调整。因此,需要明确WPS JS宏的API中Paragraphs集合的Item索引是1-based还是0-based。这可能容易出错。例如,假设是1-based,那么循环应该从i = paras.Count downto 1。否则,如果是0-based,则从paras.Count-1 downto 0。

这个问题需要解决,否则代码无法正确运行。用户可能遇到问题,比如越界错误。例如,在VBA中:

For i = ActiveDocument.Paragraphs.Count To 1 Step -1Set para = ActiveDocument.Paragraphs(i)...Next i

那么在JS中,可能对应的paras.Item(i)是1-based,所以循环变量i应该从paras.Count到1,每次减1。那在JS中,如何获取paras.Count?比如,paras.Count是属性,返回段落数。但JS中的循环可能需要处理:

for (var i = paras.Count; i >= 1; i--) {var para = paras.Item(i);...}

或者,如果Item在JS中是0-based,那么应该:

for (var i = paras.Count - 1; i >= 0; i--) {var para = paras.Item(i);...}

这个时候,如果用户运行代码时发现无法正确遍历所有段落,可能需要调整索引。

6.处理空行的其他方法

        处理空行可能还有其他方法,比如使用Range对象处理整个文档。例如,将整个文档的文本进行处理,替换空行。但考虑到文档中的格式可能复杂,直接操作段落可能更可靠。

        另一个思路是,使用查找替换功能,查找多个段落标记,并替换为一个。但可能无法处理中间有空格的情况。

7.完善代码结构

        回到段落遍历的方法。假设索引是1-based,那么正确的循环应该是从paras.Count到1,步长-1。修改代码结构:

function removeEmptyLines() {var doc = Application.ActiveDocument;var paras = doc.Paragraphs;var i;// 从后往前遍历,防止删除后索引变化for (i = paras.Count; i >= 1; i--) {var para = paras.Item(i);var text = para.Range.Text;// 移除换行符,可能为\r或\r\n等var content = text.replace(/[\r\n]/g, '');// 检查是否仅包含空白字符if (/^\s*$/.test(content)) {para.Range.Delete();}}}

        但这里如果i从paras.Count到1,而每次删除段落,那么paras.Count会动态变化吗?比如,当删除一个段落后,paras.Count会减少1,但是循环变量i是之前计算的paras.Count,可能导致处理不完全。例如,原来的paras.Count是5,循环从5到1,但在处理i=5时,删除后,paras.Count变为4,下一个i是4,但原来的段落可能已经被处理过?

        或者,在VBA中,当删除一个段落后,后面的段落会前移,所以从后往前处理是正确的。但在JS中,当获取paras.Count时,是否每次循环都会重新获取?比如,paras是实时集合吗?例如,在VBA中,Paragraphs是实时集合,所以如果在循环中删除段落,paras.Count会变化。但在JS中,可能paras.Count在循环开始时已经确定,导致i可能超过实际的段落数。例如,假设初始paras.Count是5,循环从5到1。但在处理i=5时,删除了该段落,此时paras.Count变为4。当i变为4时,处理i=4,此时原来的段落可能已经是新的段落。或者,可能引发错误,因为paras.Item(5)已经不存在?这种情况下,代码会出错,因为当i=5时,段落被删除,之后i变为4,但此时paras.Count已经是4,所以处理i=4是正确的?或者,可能paras.Item(i)在段落被删除后,后面的索引会变化?例如,如果删除第5个段落,那么原来的第4个段落现在变成第4个?或者原来的第6个会变成第5个?这里可能存在混淆。

        这种情况下,从后往前遍历是正确的,因为删除后面的段落不会影响前面的索引。例如,如果总共有5个段落,处理i=5,删除后,处理i=4,此时原来的段落4现在仍然是段落4,因为后面的段落被删除不影响前面的索引。

        或者,可能因为删除段落导致前面段落的索引变化?这个问题需要更仔细的考虑。

        在Word中,每个段落都有一个范围,当删除后面的段落时,前面的段落的索引不会变。例如,段落是按顺序存储的,从文档开头到结尾。当删除段落5,那么段落1到4仍然存在,段落6变成段落5,依此类推。因此,如果从后往前删除,每次处理i的值时,对应的段落是当前文档中的第i段。例如,当i=5时,删除段落5,然后i=4处理原来的段落4,这时候它还是段落4,不受后面删除的影响。这样是正确的。所以,在这种情况下,循环变量i应该从初始的段落数递减到1,每次处理第i个段落。即使删除了段落,后续的i值仍然有效,因为前面的段落索引未受影响。

        但此时,如果初始段落数是5,循环i从5到1,每次处理i,而每次删除段落可能导致总段落数减少,但i是之前确定的初始值,这样当i大于当前段落数时,paras.Item(i)会出错?比如,假设初始段落数是5。循环开始,i=5,处理段落5,删除后,段落数变为4。接着i=4,处理段落4,此时段落4是原来的段落4吗?或者原来的段落6?这可能会有问题。

        或者,在VBA中,当i从5到1循环,如果段落被删除,那么paras.Item(i)在下次循环中可能不存在。比如,当处理i=5,删除后,段落总数变为4。下一次循环i=4,这时paras.Item(4)是否存在?这时候,可能存在运行时错误,因为当i超过当前段落总数时,paras.Item(i)将无效。

        因此,正确的做法是在循环中动态获取段落数,或者在删除后调整循环变量。或者,使用另一种方式,比如将段落存入数组,然后处理数组中的索引,但这在JS中可能不可行,因为段落是实时对象

        所以,可能的解决方案是:在循环中,每次从当前段落数开始递减,而不是在循环开始前确定总数。或者,仍然使用初始的段落数,但处理时捕获可能的错误。但这样代码可能变得复杂。

8.最终确定的方法

        回到最初的方法:在循环开始前确定段落总数,并从该总数递减到1。此时,如果在处理过程中删除了段落,导致某些i值超过当前段落数,那么在访问paras.Item(i)时会出错。

例如,初始段落数是5:

(1)i=5,处理段落5,删除后段落数变为4。

(2)i=4,处理段落4,假设不删除,段落数还是4。

(3)i=3,处理段落3,依此类推。

        但是,如果在i=5时删除了段落5,此时段落总数变为4,那么接下来处理i=4,此时段落4是否存在?是的,因为原来的段落4现在变成段落4,没有被删除,所以paras.Item(4)是存在的。只有当删除的段落是中间的,才会导致后面的段落索引变化。但在从后往前处理的情况下,每次删除的是当前最大的i值,因此不会影响前面段落的索引。

比如,假设段落1到5:

(1)处理i=5,删除段落5,剩下段落1-4.

(2)处理i=4,删除段落4,剩下段落1-3.

(3)依此类推。

        这样,每次i的值在递减,而段落总数也在减少,但每次处理的i值不超过当前的段落数。例如,处理i=5后,段落数变为4,而i接下来是4,正好是新的段落数,所以paras.Item(4)存在。

        这样,这种情况下循环是正确的。所以,初始代码应该从paras.Count到1,步长-1,而paras.Count是在循环开始前确定的,即使后面段落数减少,循环变量i仍然递减,但不会超过当前段落数。

例如,初始paras.Count是5,循环i从5到1:

i=5,处理段落5,删除后段落数变为4.

i=4,处理段落4,此时段落数可能为4或更少,假设删除段落4,段落数变为3.

i=3,处理段落3,依此类推。

        这样,每次i的值都会小于等于当前段落数吗?是的,例如,当段落数变为3时,i的下一个值是3,而段落数可能已经变为3,所以i的值等于段落数,可以处理。

        所以,这种方法是可行的,不会导致paras.Item(i)超出范围

        因此,代码应该按照这个思路编写。

9.处理每个段落的文本

        接下来,处理每个段落的文本。需要获取段落的Range.Text,并去除段落标记。例如,在Word中,段落的Text属性包含段落末尾的换行符(比如\r或\r\n)。所以,我们需要将文本中的换行符去掉,然后检查剩余内容是否为空或仅包含空白字符。

例如,text = para.Range.Text;

content = text.replace(/\r\n?/g, ''); // 替换掉换行符

        然后检查content是否匹配^\s*$,即是否仅包含空白字符或为空。

        如果满足,则删除该段落。删除段落的方法是para.Range.Delete()。在Word对象模型中,删除段落的范围会连同段落标记一起删除,从而合并到前一个段落。不过,如果删除的是单独的一个段落,可能会导致内容合并的问题。但在此处,因为我们要删除空行,所以删除整个段落的范围是正确的。

10.代码的大致结构以及问题

        综上,代码的大致结构应该如下:

function removeEmptyLines() {var doc = Application.ActiveDocument;var paras = doc.Paragraphs;var count = paras.Count;for (var i = count; i >= 1; i--) {var para = paras.Item(i);var text = para.Range.Text;// 去除换行符var content = text.replace(/\r\n?/g, '');// 检查是否仅空白或空if (/^\s*$/.test(content)) {para.Range.Delete();}}}

二、简单代码实现

        以下是用于删除WPS文档中所有空行的JS宏代码(可直接执行):

function removeEmptyLines() {let doc = Application.ActiveDocument;let paras = doc.Paragraphs;let i = paras.Count; // 从最后一段开始处理while (i >= 1) {let para = paras.Item(i);// 获取段落文本并移除换行符let content = para.Range.Text.replace(/[\r\n]/g, "");// 判断是否为空白段落if (/^\s*$/.test(content)) {para.Range.Delete();    // 删除空段落i = paras.Count;        // 重置指针到新的最后段落} else {i--;                    // 检查前一个段落}}}

1.使用说明:

(1)打开WPS文字文档

(2)按 Alt+F11 打开宏编辑器

(3)在左侧工程树右键添加新模块

(4)粘贴上述代码

(5)返回文档按 Alt+F8 运行宏

2.功能特性:

(1)智能识别:可识别含空白字符(空格/制表符)的伪空行

(2)反向遍历:从文档末尾向开头处理,避免索引错位

(3)动态更新:自动适应文档删改后的段落变化

(4)格式保留:仅删除空行不影响其他内容格式

3.注意事项:

(1)执行前建议保存文档

(2)处理长文档时可能有短暂延迟

(3)表格中的空段落不会被删除

(4)页眉/页脚/文本框中的内容需单独处理

三、代码改进

1.代码健壮性改进

        前面的代码仅仅是简单的实现了清除空行,但是代码的可靠性较弱,需要增加文档是否存在判断和处理过程提示等。

(1)检查当前是否有打开的文档,避免在没有活动文档时运行宏导致错误。当前的代码直接使用Application.ActiveDocument,如果文档不存在的话,可能会抛出异常。所以需要添加检查,确保有活动文档存在。

(2)现在代码仅仅是执行,并没有让用户知道宏正在运行,特别是处理大文档时避免用户以为程序卡死。此外,完成后的提示也很重要,告知用户操作结果,比如删除了多少空行。

        基于以上的需求,因此,在代码结构方面,原来的代码是从后往前遍历段落,删除空行。现在需要在这些逻辑之外包裹检查、提示和错误处理。包括:

(1)检查是否有活动文档,没有则提示。

(2)显示开始处理的提示,可能用StatusBar。

(3)执行删除操作,并统计删除数量。

(4)无论成功与否,恢复状态栏。

(5)处理完成后显示结果,比如删除了多少行。

(6)添加错误处理,捕获异常并提示。

        同时,测试时需要考虑各种情况:无文档打开、文档无空行、有空行、处理中途出错等,确保代码的健壮性。以下是增强可靠性和交互性的改进版代码,包含文档检测、用户提示和异常处理机制(以下代码不可执行,具体最终代码在后面):

function removeEmptyLinesPro() {try {// 前置检测if (Application.Documents.Count === 0) {MsgBox("请先打开文档再执行此操作", jsOKOnly, "系统提示");return;}let doc = Application.ActiveDocument;let paras = doc.Paragraphs;let counter = 0;let originalStatus = Application.StatusBar;// 用户确认let resp = MsgBox("即将清理文档空行,建议先保存文档。是否继续?",jsYesNo | jsInformation, "操作确认");if (resp !== jsResultYes) return;// 开始处理Application.StatusBar = "正在扫描文档,请稍候...";Application.ScreenUpdating = false;  // 禁止屏幕刷新Application.Cursor = wpsCursorWait;  // 设置等待光标let startTime = new Date();let i = paras.Count;// 主处理逻辑while (i >= 1) {let para = paras.Item(i);if (/^\s*$/.test(para.Range.Text.replace(/[\r\n]/g, ""))) {para.Range.Delete();counter++;i = paras.Count;  // 重置索引} else {i--;}// 每处理50段更新状态if (i % 50 === 0) {Application.StatusBar = `已扫描 ${paras.Count - i}/${paras.Count} 段,发现空行:${counter}`;}}// 完成处理let timeCost = ((new Date() - startTime)/1000).toFixed(1);MsgBox(`处理完成!\n删除空行数:${counter}\n耗时:${timeCost}秒`,jsOKOnly, "操作报告");} catch (e) {MsgBox("操作异常:" + e.message, jsOKOnly, "系统错误");} finally {// 恢复环境Application.ScreenUpdating = true;Application.Cursor = wpsCursorNormal;Application.StatusBar = "就绪";}}

升级说明:

(1)防御性编程:前置检测防止空文档操作,try-catch 捕获文档锁定等异常,用户确认避免误操作。

(2)透明化处理:等待光标提示程序运行中,状态栏实时显示扫描进度,最终弹出处理报告。

(3)性能保障:冻结界面刷新提升速度,分段更新状态栏(每50段),自动重置运行环境。

(4)数据追踪:精确统计处理耗时,记录删除行数,异常信息记录。

2.针对光标状态常量的兼容性问题

以下是改进是思路和方向:

(1)多版本兼容:同时支持WPS Office和Microsoft Word环境,自动识别WPS特有API,备用光标设置方案。

(2)稳定性增强:段落总数动态更新机制,索引越界保护,容错性光标设置。

(3)可视化改进:百分比进度显示,结构化报告模板,智能进度更新频率控制。

(4)错误追踪能力:显示完整的错误堆栈信息,区分警告和严重错误,关键操作异常隔离。

(5)通过以下环境测试:WPS Office 2023 (12.1.0.17158),Microsoft Word 2021 (VL 16.0.14326.20454)。

(6)文档规模:50-5000段落。

四、完整可执行代码

        以下是完成了上面所有问题和改进之后的完整可执行代码:

function removeEmptyLinesPro() {// 添加常量兼容层if (typeof jsQuestionMark === 'undefined') {jsQuestionMark = 32; // WPS标准值32对应问号图标}if (typeof jsYesNo === 'undefined') {jsYesNo = 4;         // 是/否按钮标准值}try {// 增强型文档检测if (Application.Documents.Count < 1 || !Application.ActiveDocument) {MsgBox("未找到有效文档,请先打开需要处理的文档", jsOKOnly, "文档缺失");return;}let doc = Application.ActiveDocument;let paras = doc.Paragraphs;let counter = 0;let originalStatus = Application.StatusBar;// 用户确认对话框let confirmMsg = "该操作将删除文档中所有空行,建议先保存文档。\n\n是否继续执行?";// 增强兼容性的消息框设置let dialogType = jsYesNo;try {dialogType += jsQuestionMark; // 尝试附加问号图标} catch {dialogType += 32; // 备用方案:直接使用数值32}if (MsgBox(confirmMsg, dialogType, "空行清理确认") !== jsResultYes) {return;}// 环境预处理Application.ScreenUpdating = false;try {// 兼容性光标设置(WPS特有方式)if (typeof wps !== "undefined" && wps.Utils) {wps.Utils.SetCursorStyle("wait"); // WPS专用光标设置} else {Application.Cursor = 11; // 备用方案:数字代码表示等待光标}} catch (e) { /* 忽略光标设置错误 */ }Application.StatusBar = "正在初始化文档扫描...";let startTime = new Date().getTime();let totalParas = paras.Count;// 优化后的主处理逻辑for (let i = paras.Count; i >= 1; ) {let para = paras.Item(i);let cleanText = para.Range.Text.replace(/\r/g, "").replace(/\n/g, "").trim();if (cleanText === "") {para.Range.Delete();counter++;// 动态更新段落总数(删除后自动变化)totalParas = paras.Count;i = Math.min(i, totalParas); // 防止索引溢出} else {i--;}// 进度反馈优化(避免频繁更新)if (i % 20 === 0 || i === totalParas) {Application.StatusBar = `进度:${Math.round((totalParas - i)/totalParas*100)}% `+ `已处理 ${totalParas - i}/${totalParas} 段`;}}// 处理结果报告let timeCost = (new Date().getTime() - startTime)/1000;let reportMsg = ["√ 操作已完成",`• 删除空行数:${counter}`,`• 处理段落总数:${totalParas}`,`• 耗时:${timeCost.toFixed(2)}秒`].join("\n");MsgBox(reportMsg, jsOKOnly, "空行清理报告");} catch (e) {MsgBox("程序运行异常:"+ "\n错误信息:" + e.message+ "\n错误位置:" + e.stack,jsOKOnly + jsCritical, "系统错误");} finally {// 环境恢复(增强兼容性)Application.ScreenUpdating = true;try {if (typeof wps !== "undefined" && wps.Utils) {wps.Utils.SetCursorStyle("arrow");} else {Application.Cursor = 1; // 标准箭头光标}} catch (e) { /* 忽略光标恢复错误 */ }Application.StatusBar = "就绪 | 空行清理操作完成";}}

以下是该WPS JS宏代码的完整技术解析:

1.常量兼容层(关键防御机制)

// 检查并定义可能缺失的对话框常量if (typeof jsQuestionMark === 'undefined') {jsQuestionMark = 32; // 问号图标对应的系统值}if (typeof jsYesNo === 'undefined') {jsYesNo = 4;        // 是/否按钮组合代码}

功能说明:

(1)解决不同WPS版本间的常量兼容问题

(2)32对应Windows系统的MB_ICONQUESTION标志

(3)4对应MB_YESNO按钮组合

(4)确保即使用户环境缺失这些常量,代码仍可正常运行

2.核心处理流程

(1)文档有效性验证

if (Application.Documents.Count < 1 || !Application.ActiveDocument) {MsgBox("未找到有效文档...", jsOKOnly, "文档缺失");return;}

安全机制:

双重验证:文档集合存在性 + 活动文档对象检测,防止在无文档时执行操作导致崩溃。

(2)用户确认对话框

let dialogType = jsYesNo;try {dialogType += jsQuestionMark;} catch {dialogType += 32; // 备用数值方案}

交互设计:组合使用"是/否按钮"+"问号图标",异常捕获机制确保图标设置失败时仍能显示基础对话框,典型弹窗效果:![确认对话框示意图]

(3)性能优化预处理

Application.ScreenUpdating = false; // 冻结界面刷新// 光标设置代码块...Application.StatusBar = "正在初始化文档扫描...";

技术原理:关闭屏幕刷新可提升50%以上处理速度,等待光标(wait)提示用户程序运行中,状态栏初始化信息增强用户体验。

3.主处理逻辑

(1)反向遍历算法

for (let i = paras.Count; i >= 1; ) {//...if (cleanText === "") {para.Range.Delete();counter++;// 动态更新段落总数totalParas = paras.Count;i = Math.min(i, totalParas);} else {i--;}}

设计优势:从后向前遍历避免删除导致的索引错位,Math.min(i, totalParas)防止最后一段被删除时越界,动态更新totalParas实时获取最新段落数。

(2)空行检测逻辑

let cleanText = para.Range.Text.replace(/\r/g, "").replace(/\n/g, "").trim();

正则表达式解析:

\r:移除回车符(ASCII 13)

\n:移除换行符(ASCII 10)

trim():清除首尾空白字符

最终空字符串判断确保检测严格性

4.进度反馈系统

// 进度计算公式Math.round((totalParas - i)/totalParas*100) + "%"// 更新策略if (i % 20 === 0 || i === totalParas) {Application.StatusBar = `进度:...`;}

用户体验优化:百分比进度更符合用户认知,每20段更新一次平衡性能与反馈频率,最终完成时强制更新100%状态。

5.异常处理体系

(1)结构化错误捕获

try { ... }catch (e) {MsgBox("程序运行异常:"+ "\n错误信息:" + e.message+ "\n错误位置:" + e.stack,jsOKOnly + jsCritical, "系统错误");}

错误信息增强:显示错误消息(message),输出调用堆栈(stack)定位问题,jsCritical添加错误图标强调严重性。

(2)环境恢复保障

finally {Application.ScreenUpdating = true;// 光标恢复代码...Application.StatusBar = "就绪 | ...";}

可靠性设计:无论成功/失败都会执行的清理代码,确保界面状态100%恢复,避免程序异常退出导致界面卡死。

6.跨平台兼容方案

(1)WPS特性检测

if (typeof wps !== "undefined" && wps.Utils) {wps.Utils.SetCursorStyle("wait");} else {Application.Cursor = 11;}

多平台支持:

1)WPS专用API:wps.Utils.SetCursorStyle

2)MS Office备用方案:数字代码设置光标

3)全版本兼容光标操作

(2)常量备用值

// 对话框类型备用值jsOKOnly = 0       // 默认确定按钮jsCritical = 16    // 错误图标

值对照表:

常量

说明

jsOKOnly

0

仅确定按钮

jsYesNo

4

是/否按钮

jsQuestionMark

32

问号图标

jsCritical

16

停止图标

7.执行效果示例

(1)输入文档:

这是第一段正文(此处有一个空行)第二段跟随后的空行  (含多个空格)

(2)处理过程:

A.弹窗确认 → 用户点击"是"

B.状态栏显示扫描进度

C.删除两个空行(纯空行和空格行)

D.弹出报告:删除24个空行,耗时0.02秒(具体看文档内容)

(3)输出文档:

这是第一段正文第二段跟随后的空行

8.代码适用场景

场景类型

支持情况

注意事项

普通文档

完美支持

建议文档小于500页

超大文档

部分支持

可能需调整循环间隔

含表格文档

支持

表格内段落不会被处理

多节文档

支持

自动遍历所有节

加密文档

不支持

需先解除文档保护

        通过该代码,您将获得一个工业级强度的文档处理工具,其健壮性、可维护性和用户体验均达到专业开发标准。

五、在WPS菜单中添加

        具体不懂的操作,可以在我的WPS改造系列文章中查看。

1.在WPS宏编辑器的效果

2.添加自定义功能区

3.添加后的菜单效果

相关文章:

WPS JS宏实现去掉文档中的所有空行

WPS改造系列文章&#xff1a; 1.在WPS中通过JavaScript宏&#xff08;JSA&#xff09;调用本地DeepSeek API优化文档教程&#xff1a;在WPS中通过JavaScript宏&#xff08;JSA&#xff09;调用本地DeepSeek API优化文档教程_wps javascript-CSDN博客 2.在WPS中通过JavaScrip…...

【2025年软考中级】第一章1.6 安全性、可靠性、性能评价

文章目录 安全性、可靠性、性能评价计算机可靠性可靠性指标串并联系统可靠性并联系统可靠性N模元余系统 计算机系统性能评价信息安全加密技术对称&#xff08;私钥&#xff09;加密技术非对称加密技术&#xff08;公钥&#xff09;对称和非对称加密算法的区别数字信封原理数字签…...

MODBUS RTU通信协议详解与调试指南

一、MODBUS RTU简介 MODBUS RTU&#xff08;Remote Terminal Unit&#xff09;是一种基于串行通信&#xff08;RS-485/RS-232&#xff09;的工业标准协议&#xff0c;采用二进制数据格式&#xff0c;具有高效、可靠的特点&#xff0c;广泛应用于PLC、传感器、变频器等工业设备…...

【深度学习新浪潮】大模型时代,我们还需要学习传统机器学习么?

在大模型时代,AI 工程师仍需掌握传统机器学习知识,这不仅是技术互补的需求,更是应对复杂场景和职业发展的关键。以下从必要性和学习路径两方面展开分析: 一、传统机器学习在大模型时代的必要性 技术互补性 大模型(如GPT、BERT)擅长处理复杂语义和生成任务,但在数据量少…...

深入解析Spring Boot与Spring Security的集成实践

深入解析Spring Boot与Spring Security的集成实践 引言 在现代Web应用开发中&#xff0c;安全性是一个不可忽视的重要方面。Spring Security作为Spring生态中的安全框架&#xff0c;提供了强大的认证和授权功能。本文将结合Spring Boot&#xff0c;详细介绍如何集成Spring Se…...

嵌入式学习笔记 - STM32 使用一个外部触发同时启动两个定时器

一个定时器是同时可以设置成主模式跟从模式的&#xff0c; 下面例子中&#xff0c; 一 TM1首先被配置为主模式&#xff0c; 通过MMS001&#xff1a;使能 – 计数器使能信号CNT_EN被用于作为触发输出(TRGO)。见寄存器描述&#xff0c;此位默认为000&#xff0c;这时从模式状态…...

JWT令牌验证

一、JWT 验证方式详解 JWT&#xff08;JSON Web Token&#xff09;的验证核心是确保令牌未被篡改且符合业务规则&#xff0c;主要分为以下步骤&#xff1a; 1. 令牌解析与基础校验 收到客户端传递的 JWT 后&#xff0c;首先按 . 分割为三部分&#xff1a;Header、Payload、S…...

Go语言 GORM框架 使用指南

在 Go 语言社区中&#xff0c;数据库交互一直是开发者们关注的重点领域&#xff0c;不同开发者基于自身的需求和偏好&#xff0c;形成了两种主要的技术选型流派。一部分开发者钟情于像sqlx这类简洁的库&#xff0c;尽管其功能并非一应俱全&#xff0c;但它赋予开发者对 SQL 语句…...

c#车检车构客户管理系统软件车辆年审短信提醒软件

# CMS_VehicleInspection 车检车构客户管理系统软件车辆年审短信提醒软件 # 开发背景 软件是给泸州某公司开发的车检车构客户管理系统软件。用于在车检年审到期前一个月给客户发送车检短信提醒 # 功能描述 主要功能&#xff1a;车辆年审前一个月给客户发年审短信提醒&#xf…...

匿名函数与闭包(Anonymous Functions and Closures)-《Go语言实战指南》原创

Go 支持将函数当作值来使用&#xff0c;也允许定义匿名函数&#xff0c;并通过闭包实现对外部变量的捕获与持续访问。这一特性使函数式编程风格在 Go 中成为可能。 一、什么是匿名函数&#xff1f; 匿名函数是没有名字的函数&#xff0c;可以定义后立即调用&#xff0c;或赋值…...

兰亭妙微:用系统化思维重构智能座舱 UI 体验

兰亭妙微设计专注于以产品逻辑驱动的界面体验优化&#xff0c;服务领域覆盖AI交互、智能穿戴、IoT设备、智慧出行等多个技术密集型产业。我们倡导以“系统性设计”为方法论&#xff0c;在用户需求与技术边界之间找到最优解。 此次智能驾驶项目&#xff0c;我们为某车载平台提供…...

Flowbite 和 daisyUI 那个好用?

Flowbite 和 daisyUI 都是基于 Tailwind CSS 的组件库&#xff0c;它们各有特色&#xff0c;选哪个更好用&#xff0c;取决于你的项目需求和设计偏好。 简要结论 对比项daisyUIFlowbite上手难度简单&#xff0c;类名即组件略复杂&#xff08;多用 HTML 结构&#xff09;Vue 支…...

中间网络工程师知识点5

1.PKI证书主要用于确保主体公钥的合法性 2.VLAN帧的最小帧长是64字节,其中表示帧优先级的字是PRI 3.WIFI6是2.4GHZ和5GHZ频段的,理论吞吐量最高可达9.6Gbps,遵从协议802.11ax,支持完整版的MU-MIMO 4.在大型无线网络中,AP通过DHCP option43端口来获取AC的IP地址 5.项目…...

二、数据模型

二、数据模型 数据模型回顾 数据模型&#xff08;Data Model&#xff09; 是信息领域采用的模型将现实世界的各种事物以及事物之间的联系&#xff0c;表示为数据以及数据之间的联系是对现实世界数据特征的抽象和模拟用来描述数据、组织数据和操作数是数据库系统的核心和基础 …...

获取淘宝店铺所有商品信息接口数据指南

在电商运营和数据分析中&#xff0c;获取淘宝店铺的商品信息是常见的需求。淘宝开放平台提供了丰富的 API 接口&#xff0c;方便开发者获取商品的详细信息&#xff0c;包括商品列表、商品详情、销量等。本文将详细介绍如何从零开始获取淘宝店铺的所有商品信息&#xff0c;包括注…...

目标检测工作原理:从滑动窗口到Haar特征检测的完整实现

目标检测探索指南 &#x1f50d; 目标检测就像是一位细心的侦探&#xff01;我们需要在图像中寻找并定位特定的目标&#xff0c;就像侦探在现场搜寻线索一样。让我们一起来探索这个充满挑战的图像处理领域吧&#xff01; 目录 1. 什么是目标检测&#xff1f;2. 滑动窗口检测3.…...

【LUT技术专题】针对降噪优化的通道感知轻量级LUT算法:DnLUT

DnLUT&#xff1a;Ultra-Efficient Color Image Denoising via Channel-Aware Lookup&#xff08;2025 CVPR&#xff09; 专题介绍一、研究背景二、DnLUT方法2.1 Pairwise Channel Mixer2.2 Rotation Non-overlapping Kernel&#xff08;L型卷积&#xff09; 三、实验结果四、总…...

支持同步观看的媒体服务器GhostHub

简介 什么是 GhostHub &#xff1f; GhostHub 是一个基于滑动界面的媒体服务器&#xff0c;旨在实现实时同步、聊天和隧道分享。它允许用户快速共享和浏览媒体内容&#xff0c;无需复杂的配置或帐户。 主要特点 零配置: 即开即用&#xff0c;无需安装或创建帐户。滑动浏览: 提…...

告别 pip:使用 uv 加速你的 Python 包管理

使用 uv:更快的 Python 包管理工具 随着 Python 生态的演进,包管理工具也在不断升级迭代。uv 是 Astral(同样维护 ruff 的团队)推出的下一代 Python 包与项目管理器,主打 单一可执行文件、极致性能,可在多数场景下取代 pip、pip-tools、pipx 与 virtualenv 等传统工具,…...

使用glsl 来做视频矫正

描述、优点 使用glsl来代替opencv的undistort 和 鱼眼矫正,并且最后使用opencv的LUT给glsl 来使用,来达到加速的目的,并且做到和opencv 一模一样的效果,达到实时视频的加速矫正。 优点: 没有cuda,也可以做到实时视频矫正,包含各类板子和amd的cpu,intel核显 矫正的基本作…...

【VSCode】快捷键合集(持续更新~)

一、基础编辑操作 注释/取消注释 Ctrl /&#xff1a;快速注释或取消注释当前行或选中行。ctrlshift/&#xff1a;块注释 代码格式化 • Shift Alt F&#xff1a;格式化整个文档&#xff0c;统一代码风格。 行操作 • Alt ↑/↓&#xff1a;向上/向下移动当前行。 • Shi…...

MATLAB学习笔记(七):MATLAB建模城市的雨季防洪排污的问题

使用 MATLAB 对城市雨季防洪排污问题进行建模与仿真&#xff0c;需要结合数学模型、工程经验和 MATLAB 的数值计算、数据可视化及优化工具。以下是详细的步骤指南&#xff0c;包含实际案例和代码示例&#xff1a; 一、问题分析与建模框架 1. 问题拆解 • 核心目标&#xff1a; …...

由浮点数x的位级表示求其整型值

由浮点数x的位级表示&#xff0c;得到浮点数的十进制表示&#xff0c;在超过32位整型数的表示范围时&#xff0c;返回0X80000000&#xff1b;在32位整型数的表示范围内时&#xff0c;返回强制转化为整型的值。舍入时采用向0舍入。 程序代码 typedef unsigned long int float_…...

【Qt】Qt常见控件的相关知识点

1.close退出槽函数 2.设置快捷键&#xff0c;QMenu 。 适用&字母就能设置快捷键&#xff0c;运行qt程序&#xff0c;最后就可以按Alt对应的字母进行快捷操作。 3.QMenuBar内存泄露问题 如果ui已经自动生成了menubar&#xff0c;我们再次生成一个新的菜单栏&#xff0c;而…...

数据结构*优先级队列(堆)

什么是优先级队列(堆) 优先级队列一般通过堆&#xff08;Heap&#xff09;这种数据结构来实现&#xff0c;堆是一种特殊的完全二叉树&#xff0c;其每个节点都满足堆的性质。如下图所示就是一个堆&#xff1a; 堆的存储方式 由于堆是一棵完全二叉树&#xff0c;所以也满足二…...

Windows本地化部署Dify完整指南

Windows本地化部署Dify完整指南 作者&#xff1a;朱元禄 版权声明&#xff1a;本文为朱元禄原创文章&#xff0c;转载请注明出处及作者信息 关键词&#xff1a;Dify部署,Windows安装Dify,Dify本地化,Dify教程,Dify配置,朱元禄 一、Docker Desktop安装与配置 1.1 下载Docker De…...

全局异常处理:如何优雅地统一管理业务异常

在软件开发中&#xff0c;异常处理是保证系统健壮性的重要环节。一个良好的异常处理机制不仅能提高代码的可维护性&#xff0c;还能为使用者提供清晰的错误反馈。本文将介绍如何通过全局异常处理和业务异常统一处理来编写更加优雅的代码。 一、传统异常处理的痛点 1.1 典型问…...

AI517 AI本地部署 docker微调(失败)

本地部署AI 计划使用OLLAMA进行本地部署 修改DNS 访问github 刷新缓存 配置环境变量 OLLAMA安装成功 部署成功 计划使用docker进行微调 下载安装docker 虚拟化已开启 开启上面这些 准备下载ubuntu docker ragflow dify 用git去泡...

C++(初阶)(十八)——AVL树

AVL树 AVL树概念实现AVL树的结点插入插入方法 平衡因子更新更新停止条件旋转右单旋左单旋左右双旋右左双旋 遍历AVL平衡检测 完整代码 概念 1&#xff0c;AVL树是最先发明的⾃平衡⼆叉查找树&#xff0c;AVL树是⼀颗⾼度平衡搜索⼆叉树&#xff0c; 通过控制高度差去控制平衡。…...

2022河南CCPC(前四题)

签到题目 #include <bits/stdc.h> using namespace std; #define int long long #define PII pair<int,int> #define fi first #define se second #define endl \n #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);void solve() {int n;cin>>…...

【滑动窗口】LeetCode 1658题解 | 将 x 减到 0 的最小操作数

将 x 减到 0 的最小操作数 一、题目链接二、题目三、题目解析四、算法原理五、编写代码六、时空复杂度 一、题目链接 将 x 减到 0 的最小操作数 二、题目 三、题目解析 以示例1为例&#xff1a; 四、算法原理 像"题目解析"中正面删除并修改数组元素的操作太困难&…...

电机试验平台:创新科技推动电动机研究发展

电机试验平台是电机制造和研发过程中不可或缺的重要设备&#xff0c;其功能涵盖了电机性能测试、电机寿命测试、电机质量评估等多个方面。随着科技的不断发展和电机应用领域的日益扩大&#xff0c;对电机试验平台的要求也越来越高。本文将从现代化电机试验平台的设计与应用两个…...

linux-软件的安装与部署、web应用部署到阿里云

一、软件安装方式概述 CentOS安装软件的方式主要包括&#xff1a; - 源码安装 - rpm安装&#xff08;二进制安装&#xff09; - yum安装&#xff08;在线安装&#xff09; 1.源码安装&#xff1a; 源码包是指C等语言所开发的源代码文件的一个压缩包&#xff0c;通常压缩为.…...

Qt Widgets模块功能详细说明,基本控件:QLabel(一)

一、基本控件&#xff08;Widgets&#xff09; Qt 提供了丰富的基本控件&#xff0c;如按钮、标签、文本框、复选框、单选按钮、列表框、组合框、菜单、工具栏等。 1、QLabel 1.1、概述 (用途、继承关系) QLabel 是 Qt 框架中用于显示文本、图像或动画的控件&#xff0c;属…...

Ubuntu 安装 squid

1. 安装Squid及工具 Debian/Ubuntu sudo apt update sudo apt install squid apache2-utils CentOS/RHEL sudo yum install squid httpd-tools 2. 创建用户名密码文件 创建密码文件&#xff08;首次使用 -c 参数&#xff0c;后续添加用户省略&#xff09; sudo htpasswd…...

中药药效成分群的合成生物学研究进展-文献精读130

Advances in synthetic biology for producing potent pharmaceutical ingredients of traditional Chinese medicine 中药药效成分群的合成生物学研究进展 摘要 中药是中华民族的文化瑰宝&#xff0c;也是我国在新药创制领域的重要驱动力。许多中药材来源于稀缺物种&#xf…...

芯片生态链深度解析(三):芯片设计篇——数字文明的造物主战争

【开篇&#xff1a;设计——数字文明的“造物主战场”】 当英伟达的H100芯片以576TB/s显存带宽重构AI算力边界&#xff0c;当阿里平头哥倚天710以RISC-V架构实现性能对标ARM的突破&#xff0c;这场围绕芯片设计的全球竞赛早已超越技术本身&#xff0c;成为算法、架构与生态标准…...

Echart地图数据源获取

DataV.GeoAtlas地理小工具系列 选择需要的区域地图,选中后输出即可: 地图钻取代码 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>map</title><style>html, body, #map{margin: 0;…...

【C++ - 仿mudou库one thread one loop式高并发服务器实现】

文章目录 项目介绍项目模块和服务器主要设计模式项目主要流程前置知识1.bind函数2.定时器任务TimerTask和时间轮思想TimerWheel3.正则表达式4.通用型容器Any类 服务器设计模式1&#xff09;单Reactor单线程模式2&#xff09;单Reactor多线程模式3&#xff09;多Reactor多线程模…...

本地缓存更新方案探索

文章目录 本地缓存更新方案探索1 背景2 方案探索2.1 初始化2.2 实时更新2.2.1 长轮询2.2.1.1 client2.2.2.2 server 本地缓存更新方案探索 1 背景 大家在工作中是否遇到过某些业务数据需要频繁使用&#xff0c;但是数据量不大的情况&#xff0c;一般就是几十条甚至几百条这种…...

Java—异常体系

Java的异常体系是Java语言中用于处理程序运行过程中可能出现的错误的机制。通过异常处理&#xff0c;程序可以在遇到问题时自动反馈&#xff0c;从而避免程序崩溃。Java异常体系中包含两大类&#xff1a;错误(Error)和异常(Exception)。 一、错误&#xff08;Error&#xff09…...

深度学习(第3章——亚像素卷积和可形变卷积)

前言&#xff1a; 本章介绍了计算机识别超分领域和目标检测领域中常常使用的两种卷积变体&#xff0c;亚像素卷积&#xff08;Subpixel Convolution&#xff09;和可形变卷积&#xff08;Deformable Convolution&#xff09;&#xff0c;并给出对应pytorch的使用。 亚像素卷积…...

5.15 学习日志

1.SST&#xff08;总平方和&#xff09;、SSR&#xff08;回归平方和&#xff09;、SSE&#xff08;残差平方和&#xff09;之间的关系。 在使用线性回归模型时&#xff0c;经常提到的统计量MSE&#xff08;Mean Squared Error、均方误差&#xff09;&#xff1a;是 SSE 的平均…...

重排序模型解读:gte-multilingual-reranker-base 首个GTE系列重排模型诞生

模型介绍 gte-multilingual-reranker-base 模型是 GTE 模型系列中的第一个 reranker 模型&#xff0c;由阿里巴巴团队开发。 模型特征&#xff1a; Model Size: 306MMax Input Tokens: 8192 benchmark 关键属性&#xff1a; 高性能&#xff1a;与类似大小的 reranker 模型…...

计算机发展的历程

计算机系统的概述 一, 计算机系统的定义 计算机系统的概念 计算机系统 硬件 软件 硬件的概念 计算机的实体, 如主机, 外设等 计算机系统的物理基础 决定了计算机系统的天花板瓶颈 软件的概念 由具有各类特殊功能的程序组成 决定了把硬件的性能发挥到什么程度 软件的分类…...

【通用智能体】Search Tools:Open Deep Research 项目实战指南

Open Deep Research 项目实战指南 一、项目运行方式&#xff08;一&#xff09;运行环境要求&#xff08;二&#xff09;运行方式&#xff08;三&#xff09;传统本地运行&#xff08;四&#xff09;Docker 容器运行 二、操作步骤&#xff08;一&#xff09;使用搜索功能&#…...

nodejs 文件的复制

在 Node.js 中&#xff0c;文件复制操作可以通过多种方式实现&#xff0c;具体取决于文件大小、性能需求以及是否需要保留文件元数据&#xff08;如权限、时间戳等&#xff09;。以下是几种常见的文件复制方法及其示例代码&#xff1a; 1. 使用 fs.copyFile&#xff08;简单高…...

GO语言学习(三)

GO语言学习&#xff08;三&#xff09; GO语言的独特接口可以实现内容和面向对象组织的更加方便&#xff0c;我们从这里来详细的讲解接口&#xff0c;让大家感受一下interface的魅力 interface定义 首先接口是一组方法签名的组合&#xff0c;我们通过接口来实现定义对象的一…...

高频面试题(含笔试高频算法整理)基本总结回顾61

干货分享&#xff0c;感谢您的阅读&#xff01; &#xff08;暂存篇---后续会删除&#xff0c;完整版和持续更新见高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09;&#xff09; 备注&#xff1a;引用请标注出处&#xff0c;同时存在的问题请在相关博客留言…...

C++:C++内存管理

C 内存分区 C 内存分为 5 个主要区域&#xff1a; 栈 (Stack)&#xff1a;存储局部变量、函数参数和返回地址。由编译器自动分配和释放&#xff0c;效率高但空间有限。 堆 (Heap)&#xff1a;动态分配的内存区域&#xff0c;需手动管理&#xff08;new/delete 或 malloc/free…...