WPS中代碼段的識別方法及JS宏實現

在WPS中,文檔的基本結構可以通過對象模型來理解:

(1)Document對象:表示整個文檔

(2)Range對象:表示文檔中的一段連續區域,可以是一個字符、一個句子或整個文檔

(3)Paragraph對象:表示文檔中的段落

(4)Selection對象:表示當前用戶選中的區域

為了更高效的工作,我們希望通過WPS JS宏可以幫助你識別文檔中的代碼段并添加樣式快速實現美化文檔效果。這時,就要先確定文檔中的內容是否為代碼段,有很多的方法。

WPS改造系列文章:

1.在WPS中通過JavaScript宏(JSA)調用本地DeepSeek API優化文檔教程:在WPS中通過JavaScript宏(JSA)調用本地DeepSeek API優化文檔教程_wps js宏-CSDN博客?

2.在WPS中通過JavaScript宏(JSA)調用DeepSeek官網API優化文檔教程:在WPS中通過JavaScript宏(JSA)調用DeepSeek官網API優化文檔教程_wps js宏官方文檔-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 js宏 圖片-CSDN博客?

6.WPS中代碼段的識別方法及JS宏實現:WPS中代碼段的識別方法及JS宏實現_wps js range-CSDN博客?

一、基于標記的代碼段識別方法

1.方法概述

基于標記的方法,即通過特定的開始和結束標記來識別代碼塊。這種方法適用于已經使用特定標記(如 ```、/* */ 等)標識的代碼。

這種方法需要定義多種可能的代碼塊標記,包括常見的Markdown、HTML和注釋風格標記,使用正則表達式來查找文檔中的代碼塊,之后再為每個識別出的代碼塊應用統一的樣式,包括邊框、背景色和等寬字體。

為什么提示 “未在文檔中找到代碼塊!”?

這可能有以下幾個原因:

(1)文檔中確實沒有使用預定義標記的代碼塊

(2)代碼塊使用了其他標記或格式

(3)正則表達式匹配不夠靈活,無法識別某些格式的代碼塊

2.改進代碼塊識別的方法

以下是幾種可以改進代碼塊識別的方法:

(1)增加更多的標記類型:可以擴展codeStartMarkers和codeEndMarkers數組,添加更多可能的代碼塊標記。添加了常見的編程和 HTML 標記(如 <?php、<script 等),可以根據需要繼續擴展這個列表。

(2)基于代碼特征識別:除了標記外,還可以基于代碼的一些特征來識別,如縮進、特定關鍵字等。例如,通過檢測連續縮進的段落來識別代碼塊,設置了一個縮進閾值(4 個空格),超過這個閾值的段落會被視為代碼的一部分。例如,添加關鍵字檢測檢查段落是否以常見的代碼關鍵字(如function、if、for等)開頭,這有助于識別沒有明顯縮進但確實是代碼的段落。

(3)使用AI輔助識別:如果需要更智能的識別,可以考慮調用AI API來分析文本是否為代碼。

3.代碼示例

// 高亮代碼塊主函數 - 供自定義功能區按鈕調用function highlightCodeBlocks() {try {// 獲取當前文檔let doc = ThisDocument;if (!doc) {alert("未找到當前文檔!");return;}// 定義代碼塊樣式配置let codeBlockStyle = {borderColor: "#cccccc",borderWidth: 1,backgroundColor: "#f7f7f9",fontFamily: "Consolas, 'Courier New', monospace",fontSize: 10.5};// 獲取文檔內容let content = doc.Content.Text;// 方法1: 使用標記識別代碼塊let codeBlocks = findCodeBlocksByMarkers(content);// 方法2: 識別連續縮進的段落作為代碼塊if (codeBlocks.length === 0) {codeBlocks = findCodeBlocksByIndentation(doc);}// 如果沒有找到代碼塊,提示用戶if (codeBlocks.length === 0) {alert("未在文檔中找到代碼塊!");return;}// 處理每個代碼塊for (let block of codeBlocks) {try {// 創建Range對象let codeRange = doc.Range(block.start, block.end);// 應用代碼塊樣式applyCodeBlockStyle(codeRange, codeBlockStyle);} catch (e) {console.error(`處理代碼塊時出錯: ${e.message}`);}}alert(`成功處理 ${codeBlocks.length} 個代碼塊!`);} catch (e) {alert(`執行過程中出錯: ${e.message}`);}}// 方法1: 使用標記識別代碼塊function findCodeBlocksByMarkers(content) {// 定義代碼塊可能的開始和結束標記let codeStartMarkers = ["```", "/*", "<code>", "# 代碼開始", "// 代碼開始","<?php", "<script", "<style", "<html"];let codeEndMarkers = ["```", "*/", "</code>", "# 代碼結束", "// 代碼結束","?>", "</script>", "</style>", "</html>"];let codeBlocks = [];// 使用正則表達式查找代碼塊for (let i = 0; i < codeStartMarkers.length; i++) {let startMarker = codeStartMarkers[i];let endMarker = codeEndMarkers[i];// 構建正則表達式let regexPattern = `${escapeRegExp(startMarker)}(.*?)${escapeRegExp(endMarker)}`;let regex = new RegExp(regexPattern, 'gs');let match;while ((match = regex.exec(content)) !== null) {codeBlocks.push({start: match.index,end: match.index + match[0].length,content: match[0]});}}return codeBlocks;}// 方法2: 識別連續縮進的段落作為代碼塊function findCodeBlocksByIndentation(doc) {let codeBlocks = [];let currentCodeBlock = null;let indentThreshold = 4; // 至少4個空格的縮進// 遍歷文檔中的每個段落for (let i = 1; i <= doc.Paragraphs.Count; i++) {let para = doc.Paragraphs(i);let firstLineIndent = para.Format.FirstLineIndent;let leftIndent = para.Format.LeftIndent;let totalIndent = Math.abs(firstLineIndent) + Math.abs(leftIndent);// 檢查段落是否以常見代碼關鍵字開頭let isCodeLine = false;let paraText = para.Range.Text.trim();let codeKeywords = ["function", "class", "def", "if", "for", "while", "import", "package"];for (let keyword of codeKeywords) {if (paraText.startsWith(keyword)) {isCodeLine = true;break;}}// 如果段落有足夠的縮進或者以代碼關鍵字開頭if (totalIndent >= indentThreshold || isCodeLine) {if (!currentCodeBlock) {// 開始一個新的代碼塊currentCodeBlock = {start: para.Range.Start,end: para.Range.End,paragraphCount: 1};} else {// 擴展當前代碼塊currentCodeBlock.end = para.Range.End;currentCodeBlock.paragraphCount++;}} else if (currentCodeBlock) {// 如果當前有代碼塊且當前段落不是代碼行,則結束當前代碼塊// 只添加至少包含2個段落的代碼塊,避免誤判if (currentCodeBlock.paragraphCount >= 2) {codeBlocks.push(currentCodeBlock);}currentCodeBlock = null;}}// 添加最后一個代碼塊(如果有)if (currentCodeBlock && currentCodeBlock.paragraphCount >= 2) {codeBlocks.push(currentCodeBlock);}return codeBlocks;}// 應用代碼塊樣式function applyCodeBlockStyle(range, style) {// 應用段落格式range.ParagraphFormat.Borders.Enable = true;range.ParagraphFormat.Borders.OutsideLineStyle = wdLineStyleSingle;range.ParagraphFormat.Borders.OutsideLineWidth = style.borderWidth;range.ParagraphFormat.Borders.OutsideColor = RGBToColor(style.borderColor);// 應用字符格式range.Font.Name = style.fontFamily;range.Font.Size = style.fontSize;// 應用底紋range.Shading.BackgroundPatternColor = RGBToColor(style.backgroundColor);// 增加段落間距range.ParagraphFormat.SpaceBefore = 6;range.ParagraphFormat.SpaceAfter = 6;}// 輔助函數:將RGB顏色字符串轉換為WPS顏色值function RGBToColor(rgbStr) {// 移除可能的#符號rgbStr = rgbStr.replace('#', '');// 解析RGB值let r = parseInt(rgbStr.substring(0, 2), 16);let g = parseInt(rgbStr.substring(2, 4), 16);let b = parseInt(rgbStr.substring(4, 6), 16);// WPS使用BGR順序return r + (g << 8) + (b << 16);}// 輔助函數:轉義正則表達式特殊字符function escapeRegExp(string) {return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');}

二、基于文本特征的代碼段識別方法

如果我是直接將代碼文本復制到文檔中,沒有使用任何特殊標記(如 ``` 或縮進)。這種情況下,我們需要更智能的方式來識別代碼段。

我設計了一種基于文本特征的代碼段識別方法,通過分析文本的語言特征、結構模式和統計特性來判斷是否為代碼。這種方法不需要用戶添加任何特殊標記,只需要將純代碼文本粘貼到文檔中即可。

1.智能代碼識別原理

通過使用了以下策略來識別純文本代碼:

(1)特征庫匹配:內置了多種編程語言的特征庫,包括關鍵字、操作符和特殊符號

(2)統計分析:分析文本中代碼特征的出現頻率,超過一定閾值則認為是代碼

(3)連續段落檢查:要求代碼至少連續出現 3 行,避免誤判

(4)文本過濾:排除包含 URL、郵箱或大量中文的段落,這些通常不是代碼

(5)多語言支持:目前支持通用代碼、Python、JavaScript 和 Java,可以根據需要擴展更多語言

2.主要改進思路

(1)增強語言的特征庫:以下以JavaScript為例,添加更多JavaScript關鍵字和操作符,增加對JavaScript注釋的特殊處理。

(2)降低識別閾值:將JavaScript的最小特征匹配數從4降低到2,將連續代碼行要求從3降低到2。

(3)改進注釋處理:專門添加了對//、/*和*/的檢查,直接將注釋行識別為代碼的一部分

(4)優化中文檢測:將中文閾值從20%提高到30%,減少誤判。

3.代碼示例

// 高亮代碼塊主函數 - 供自定義功能區按鈕調用function highlightCodeBlocks() {try {// 獲取當前文檔let doc = ThisDocument;if (!doc) {alert("未找到當前文檔!");return;}// 定義代碼塊樣式配置let codeBlockStyle = {borderColor: "#cccccc",borderWidth: 1,backgroundColor: "#f7f7f9",fontFamily: "Consolas, 'Courier New', monospace",fontSize: 10.5};// 使用智能代碼識別let codeBlocks = findCodeBlocksByAI(doc);// 如果沒有找到代碼塊,提示用戶if (codeBlocks.length === 0) {alert("未在文檔中找到可識別的代碼塊!");return;}// 處理每個代碼塊for (let block of codeBlocks) {try {// 創建Range對象let codeRange = doc.Range(block.start, block.end);// 應用代碼塊樣式applyCodeBlockStyle(codeRange, codeBlockStyle);} catch (e) {console.error(`處理代碼塊時出錯: ${e.message}`);}}alert(`成功識別并處理 ${codeBlocks.length} 個代碼塊!`);} catch (e) {alert(`執行過程中出錯: ${e.message}`);}}// 智能代碼識別方法function findCodeBlocksByAI(doc) {let codeBlocks = [];let paragraphs = doc.Paragraphs;// 代碼特征庫 - 增強了JavaScript識別能力let codePatterns = [// JavaScript 特征 - 增強版{name: "javascript",keywords: ["function", "class", "let", "const", "var", "if", "else", "for", "while","return", "import", "export", "async", "await", "try", "catch", "finally","switch", "case", "default", "break", "continue", "this", "new", "delete"],operators: ["==", "===", "!=", "!==", ">=", "<=", "++", "--", "&&", "||", "=>", "=","+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|="],symbols: ["{", "}", "[", "]", "(", ")", ";", ":", ",", "`", "'", "\"", ".", "..", "...","?", "??", "?."],comments: ["//", "/*", "*/"], // 專門處理注釋minOccurrences: 2, // 降低閾值,只需滿足2個特征minConsecutive: 2 // 只需連續2行},// 通用代碼特征{name: "general",keywords: ["function", "class", "def", "return", "if", "else", "for", "while", "import", "package"],operators: ["==", "!=", "=>", "++", "--", "&&", "||", "=>", "->", "::"],symbols: ["{", "}", "[", "]", "(", ")", ";", ":", "=", ","],minOccurrences: 3,minConsecutive: 3},// Python 特征{name: "python",keywords: ["def", "class", "if", "else", "elif", "for", "while", "in", "range", "return", "import", "from", "as"],operators: ["==", "!=", ">=", "<=", "=", "+=", "-=", "*=", "/=", "%=", "//=", "**="],symbols: [":", "#", "(", ")", "[", "]", "{", "}", ","],indentation: true,minOccurrences: 3,minConsecutive: 3},// Java 特征{name: "java",keywords: ["public", "private", "protected", "class", "interface", "void", "static", "if", "else", "for", "while", "return", "import", "package"],operators: ["==", "!=", ">=", "<=", "++", "--", "&&", "||", "=", "+=", "-=", "*=", "/=", "%="],symbols: ["{", "}", "[", "]", "(", ")", ";", ":", ",", "\"", "'"],minOccurrences: 3,minConsecutive: 3}];// 分析每個段落,尋找代碼塊let currentBlock = null;let consecutiveCodeLines = 0;for (let i = 1; i <= paragraphs.Count; i++) {let para = paragraphs(i);let text = para.Range.Text.trim();// 跳過空段落if (text.length === 0) {if (currentBlock) {consecutiveCodeLines++;}continue;}// 檢查當前段落是否為代碼let isCode = analyzeParagraph(text, codePatterns);if (isCode) {consecutiveCodeLines++;if (!currentBlock) {// 開始一個新的代碼塊currentBlock = {start: para.Range.Start,end: para.Range.End,paragraphCount: 1};} else {// 擴展當前代碼塊currentBlock.end = para.Range.End;currentBlock.paragraphCount++;}} else {// 如果當前有代碼塊且當前段落不是代碼行,則結束當前代碼塊if (currentBlock && consecutiveCodeLines >= codePatterns[0].minConsecutive) {codeBlocks.push(currentBlock);}currentBlock = null;consecutiveCodeLines = 0;}}// 添加最后一個代碼塊(如果有)if (currentBlock && consecutiveCodeLines >= codePatterns[0].minConsecutive) {codeBlocks.push(currentBlock);}return codeBlocks;}// 分析段落是否為代碼 - 改進版function analyzeParagraph(text, codePatterns) {// 檢查是否包含URL、郵箱等非代碼文本if (text.match(/https?:\/\/\S+|www\.\S+|\S+@\S+\.\S+/)) {return false;}// 檢查是否包含中文(超過30%的字符是中文可能不是代碼)let chineseChars = text.match(/[\u4e00-\u9fa5]/g);if (chineseChars && chineseChars.length / text.length > 0.3) {return false;}// 特殊處理JavaScript注釋行if (text.startsWith("//") || text.startsWith("/*") || text.endsWith("*/")) {return true; // 直接認為注釋行是代碼的一部分}// 檢查是否符合任意一種代碼模式for (let pattern of codePatterns) {let score = 0;// 檢查關鍵字for (let keyword of pattern.keywords) {// 確保關鍵字是獨立的,而不是其他單詞的一部分let regex = new RegExp(`\\b${keyword}\\b`);if (regex.test(text)) {score++;}}// 檢查操作符for (let operator of pattern.operators) {if (text.includes(operator)) {score++;}}// 檢查符號for (let symbol of pattern.symbols) {if (text.includes(symbol)) {score++;}}// 檢查縮進(僅對Python)if (pattern.indentation && (text.startsWith(' ???') || text.startsWith('\t'))) {score++;}// 如果得分超過閾值,認為是代碼if (score >= pattern.minOccurrences) {return true;}}return false;}// 應用代碼塊樣式function applyCodeBlockStyle(range, style) {// 應用段落格式range.ParagraphFormat.Borders.Enable = true;range.ParagraphFormat.Borders.OutsideLineStyle = wdLineStyleSingle;range.ParagraphFormat.Borders.OutsideLineWidth = style.borderWidth;range.ParagraphFormat.Borders.OutsideColor = RGBToColor(style.borderColor);// 應用字符格式range.Font.Name = style.fontFamily;range.Font.Size = style.fontSize;// 應用底紋range.Shading.BackgroundPatternColor = RGBToColor(style.backgroundColor);// 增加段落間距range.ParagraphFormat.SpaceBefore = 6;range.ParagraphFormat.SpaceAfter = 6;}// 輔助函數:將RGB顏色字符串轉換為WPS顏色值function RGBToColor(rgbStr) {// 移除可能的#符號rgbStr = rgbStr.replace('#', '');// 解析RGB值let r = parseInt(rgbStr.substring(0, 2), 16);let g = parseInt(rgbStr.substring(2, 4), 16);let b = parseInt(rgbStr.substring(4, 6), 16);// WPS使用BGR順序return r + (g << 8) + (b << 16);}// 輔助函數:轉義正則表達式特殊字符function escapeRegExp(string) {return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');}

4.使用方法

(1)將代碼添加到WPS JS宏編輯器

(2)在自定義功能區中添加對highlightCodeBlocks函數的調用

(3)將純代碼文本復制粘貼到文檔中

(4)點擊自定義功能區中的按鈕執行宏

這個版本應該能夠識別沒有任何標記的純代碼文本,并為其添加邊框和背景色,使其在文檔中明顯區分出來。如果識別效果不理想,你可以調整findCodeBlocksByAI函數中的參數,如增加語言特征庫或調整閾值。

三、基于密度結構識別方法

如果現有的識別算法還不夠靈活,無法準確識別你的代碼。我再提供一個更簡單直接的版本,這個版本會嘗試通過代碼的密度結構來識別,而不是依賴特定的關鍵字

1.新的代碼識別方法

(1)計算每行的符號密度(如括號、分號等)

(2)檢查是否有一致的縮進模式

(3)尋找代碼特有的結構模式(如函數定義、循環等)

2.這個版本的改進

(1)基于符號密度的識別:計算每行中代碼特有的符號數量,超過閾值即認為是代碼。

(2)結構模式匹配:使用正則表達式識別常見的代碼結構(函數定義、循環等)。

(3)寬松的條件:連續2行代碼即可識別為代碼塊,允許代碼塊中包含少量空行,降低了對特定關鍵字的依賴。

(4)更好的注釋處理:直接將注釋行識別為代碼的一部分。

3.代碼示例

// 高亮代碼塊主函數 - 供自定義功能區按鈕調用function highlightCodeBlocks() {try {// 獲取當前文檔let doc = ThisDocument;if (!doc) {alert("未找到當前文檔!");return;}// 定義代碼塊樣式配置let codeBlockStyle = {borderColor: "#cccccc",borderWidth: 1,backgroundColor: "#f7f7f9",fontFamily: "Consolas, 'Courier New', monospace",fontSize: 10.5};// 使用基于結構和密度的代碼識別let codeBlocks = findCodeBlocksByStructure(doc);// 如果沒有找到代碼塊,提示用戶if (codeBlocks.length === 0) {alert("未在文檔中找到可識別的代碼塊!");return;}// 處理每個代碼塊for (let block of codeBlocks) {try {// 創建Range對象let codeRange = doc.Range(block.start, block.end);// 應用代碼塊樣式applyCodeBlockStyle(codeRange, codeBlockStyle);} catch (e) {console.error(`處理代碼塊時出錯: ${e.message}`);}}alert(`成功識別并處理 ${codeBlocks.length} 個代碼塊!`);} catch (e) {alert(`執行過程中出錯: ${e.message}`);}}// 基于結構和密度的代碼識別方法function findCodeBlocksByStructure(doc) {let codeBlocks = [];let paragraphs = doc.Paragraphs;// 代碼特征符號let codeSymbols = ["{", "}", "[", "]", "(", ")", ";", ":", "=", "+", "-", "*", "/", "%", "&", "|", "^", "!", "~", "<", ">", ","];// 代碼結構模式let codePatterns = [/^\s*function\s/,/^\s*class\s/,/^\s*(let|const|var)\s/,/^\s*if\s*\(/,/^\s*for\s*\(/,/^\s*while\s*\(/,/^\s*switch\s*\(/,/^\s*return\s/,/^\s*def\s/,/^\s*class\s/,/^\s*import\s/,/^\s*from\s/,/^\s*public\s/,/^\s*private\s/,/^\s*protected\s/];// 分析每個段落,尋找代碼塊let currentBlock = null;let consecutiveCodeLines = 0;for (let i = 1; i <= paragraphs.Count; i++) {let para = paragraphs(i);let text = para.Range.Text.trim();// 跳過空段落if (text.length === 0) {if (currentBlock) {consecutiveCodeLines++;// 空行允許,但連續空行過多則結束代碼塊if (consecutiveCodeLines > 3) {if (currentBlock.paragraphCount >= 2) {codeBlocks.push(currentBlock);}currentBlock = null;consecutiveCodeLines = 0;}}continue;}// 檢查當前段落是否為代碼let isCode = analyzeLineAsCode(text, codeSymbols, codePatterns);if (isCode) {consecutiveCodeLines++;if (!currentBlock) {// 開始一個新的代碼塊currentBlock = {start: para.Range.Start,end: para.Range.End,paragraphCount: 1};} else {// 擴展當前代碼塊currentBlock.end = para.Range.End;currentBlock.paragraphCount++;}} else {// 如果當前有代碼塊且當前段落不是代碼行,則結束當前代碼塊if (currentBlock && currentBlock.paragraphCount >= 2) {codeBlocks.push(currentBlock);}currentBlock = null;consecutiveCodeLines = 0;}}// 添加最后一個代碼塊(如果有)if (currentBlock && currentBlock.paragraphCount >= 2) {codeBlocks.push(currentBlock);}return codeBlocks;}// 分析行是否為代碼function analyzeLineAsCode(text, codeSymbols, codePatterns) {// 檢查是否包含URL、郵箱等非代碼文本if (text.match(/https?:\/\/\S+|www\.\S+|\S+@\S+\.\S+/)) {return false;}// 檢查是否包含大量中文(超過30%的字符是中文可能不是代碼)let chineseChars = text.match(/[\u4e00-\u9fa5]/g);if (chineseChars && chineseChars.length / text.length > 0.3) {return false;}// 檢查是否是注釋行if (text.startsWith("//") || text.startsWith("/*") || text.startsWith("#") || text.endsWith("*/")) {return true;}// 檢查符號密度 - 代碼通常有較高的符號密度let symbolCount = 0;for (let symbol of codeSymbols) {symbolCount += (text.split(symbol).length - 1);}// 如果符號數量超過閾值,認為是代碼if (symbolCount >= 3) {return true;}// 檢查是否匹配任何代碼結構模式for (let pattern of codePatterns) {if (pattern.test(text)) {return true;}}// 檢查是否有一致的縮進(代碼通常有一致的縮進模式)if (text.startsWith(' ???') || text.startsWith('\t')) {return true;}return false;}// 應用代碼塊樣式function applyCodeBlockStyle(range, style) {// 應用段落格式range.ParagraphFormat.Borders.Enable = true;range.ParagraphFormat.Borders.OutsideLineStyle = wdLineStyleSingle;range.ParagraphFormat.Borders.OutsideLineWidth = style.borderWidth;range.ParagraphFormat.Borders.OutsideColor = RGBToColor(style.borderColor);// 應用字符格式range.Font.Name = style.fontFamily;range.Font.Size = style.fontSize;// 應用底紋range.Shading.BackgroundPatternColor = RGBToColor(style.backgroundColor);// 增加段落間距range.ParagraphFormat.SpaceBefore = 6;range.ParagraphFormat.SpaceAfter = 6;}// 輔助函數:將RGB顏色字符串轉換為WPS顏色值function RGBToColor(rgbStr) {// 移除可能的#符號rgbStr = rgbStr.replace('#', '');// 解析RGB值let r = parseInt(rgbStr.substring(0, 2), 16);let g = parseInt(rgbStr.substring(2, 4), 16);let b = parseInt(rgbStr.substring(4, 6), 16);// WPS使用BGR順序return r + (g << 8) + (b << 16);}// 輔助函數:轉義正則表達式特殊字符function escapeRegExp(string) {return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');}

四、基于字符統計和行模式的代碼識別

到了這里,看起來我們需要更激進的代碼識別策略。我將嘗試一種基于字符分布和行模式的方法,這種方法不依賴于特定的語言關鍵字,而是分析文本的整體特征

1.這個版本的特點

(1)不依賴特定語言:通過分析字符分布和行模式來識別代碼,適用于各種編程語言

(2)統計特征分析:計算代碼特有的符號密度,分析行首縮進模式,檢查行長度分布,檢測代碼結構特征。

(3)寬松的識別條件:只要滿足符號密度、縮進、長度和結構的綜合條件即可識別,對注釋行有特殊處理,允許代碼塊中有少量空行。

(4)中文文本過濾:通過限制中文字符比例來減少誤判。

2.代碼示例

以下是一個完全重寫的代碼塊識別算法,它通過分析文本的字符分布、行長度變化和結構模式來識別代碼:

// 高亮代碼塊主函數 - 供自定義功能區按鈕調用function highlightCodeBlocks() {try {// 獲取當前文檔let doc = ThisDocument;if (!doc) {alert("未找到當前文檔!");return;}// 定義代碼塊樣式配置let codeBlockStyle = {borderColor: "#cccccc",borderWidth: 1,backgroundColor: "#f7f7f9",fontFamily: "Consolas, 'Courier New', monospace",fontSize: 10.5};// 使用基于統計特征的代碼識別let codeBlocks = findCodeBlocksByStats(doc);// 如果沒有找到代碼塊,提示用戶if (codeBlocks.length === 0) {alert("未在文檔中找到可識別的代碼塊!");return;}// 處理每個代碼塊for (let block of codeBlocks) {try {// 創建Range對象let codeRange = doc.Range(block.start, block.end);// 應用代碼塊樣式applyCodeBlockStyle(codeRange, codeBlockStyle);} catch (e) {console.error(`處理代碼塊時出錯: ${e.message}`);}}alert(`成功識別并處理 ${codeBlocks.length} 個代碼塊!`);} catch (e) {alert(`執行過程中出錯: ${e.message}`);}}// 基于統計特征的代碼識別方法function findCodeBlocksByStats(doc) {let codeBlocks = [];let paragraphs = doc.Paragraphs;// 代碼特征字符let codeChars = "{}[]();:.,+-*/%=&|!~<>\"'`";// 分析每個段落,尋找代碼塊let currentBlock = null;let consecutiveCodeLines = 0;for (let i = 1; i <= paragraphs.Count; i++) {let para = paragraphs(i);let text = para.Range.Text;// 跳過空段落if (text.trim().length === 0) {if (currentBlock) {// 空行允許,但連續空行過多則結束代碼塊consecutiveCodeLines++;if (consecutiveCodeLines > 3) {if (currentBlock.paragraphCount >= 2) {codeBlocks.push(currentBlock);}currentBlock = null;consecutiveCodeLines = 0;}}continue;}// 檢查當前段落是否為代碼let isCode = analyzeLineByStats(text, codeChars);if (isCode) {consecutiveCodeLines++;if (!currentBlock) {// 開始一個新的代碼塊currentBlock = {start: para.Range.Start,end: para.Range.End,paragraphCount: 1};} else {// 擴展當前代碼塊currentBlock.end = para.Range.End;currentBlock.paragraphCount++;}} else {// 如果當前有代碼塊且當前段落不是代碼行,則結束當前代碼塊if (currentBlock && currentBlock.paragraphCount >= 2) {codeBlocks.push(currentBlock);}currentBlock = null;consecutiveCodeLines = 0;}}// 添加最后一個代碼塊(如果有)if (currentBlock && currentBlock.paragraphCount >= 2) {codeBlocks.push(currentBlock);}return codeBlocks;}// 基于統計特征分析行是否為代碼function analyzeLineByStats(text, codeChars) {// 移除行首縮進let trimmedText = text.trim();// 檢查是否包含URL、郵箱等非代碼文本if (trimmedText.match(/https?:\/\/\S+|www\.\S+|\S+@\S+\.\S+/)) {return false;}// 檢查是否是注釋行if (trimmedText.startsWith("//") || trimmedText.startsWith("/*") ||trimmedText.startsWith("#") || trimmedText.endsWith("*/")) {return true;}// 計算代碼特征字符的比例let codeCharCount = 0;for (let i = 0; i < trimmedText.length; i++) {if (codeChars.includes(trimmedText[i])) {codeCharCount++;}}// 代碼通常有較高的符號密度let codeCharRatio = codeCharCount / trimmedText.length;// 計算行首縮進量(空格或制表符)let indentCount = 0;while (indentCount < text.length && (text[indentCount] === ' ' || text[indentCount] === '\t')) {indentCount++;}// 代碼通常有一致的縮進模式let hasIndent = indentCount > 0;// 代碼行通常不會太長也不會太短let lineLength = trimmedText.length;let isTypicalLength = lineLength > 5 && lineLength < 200;// 檢查是否有代碼特有的結構模式let hasCodeStructure =trimmedText.includes('{') || trimmedText.includes('}') ||trimmedText.includes('(') || trimmedText.includes(')') ||trimmedText.includes(';') || trimmedText.includes('=') ||trimmedText.includes(':') || trimmedText.includes(',');// 計算中文字符比例let chineseChars = text.match(/[\u4e00-\u9fa5]/g);let chineseRatio = chineseChars ? chineseChars.length / text.length : 0;// 綜合判斷:如果符號密度高、有縮進、長度適中、有代碼結構,且中文比例低,則認為是代碼return (codeCharRatio > 0.1 || hasIndent) &&isTypicalLength &&hasCodeStructure &&chineseRatio < 0.3;}// 應用代碼塊樣式function applyCodeBlockStyle(range, style) {// 應用段落格式range.ParagraphFormat.Borders.Enable = true;range.ParagraphFormat.Borders.OutsideLineStyle = wdLineStyleSingle;range.ParagraphFormat.Borders.OutsideLineWidth = style.borderWidth;range.ParagraphFormat.Borders.OutsideColor = RGBToColor(style.borderColor);// 應用字符格式range.Font.Name = style.fontFamily;range.Font.Size = style.fontSize;// 應用底紋range.Shading.BackgroundPatternColor = RGBToColor(style.backgroundColor);// 增加段落間距range.ParagraphFormat.SpaceBefore = 6;range.ParagraphFormat.SpaceAfter = 6;}// 輔助函數:將RGB顏色字符串轉換為WPS顏色值function RGBToColor(rgbStr) {// 移除可能的#符號rgbStr = rgbStr.replace('#', '');// 解析RGB值let r = parseInt(rgbStr.substring(0, 2), 16);let g = parseInt(rgbStr.substring(2, 4), 16);let b = parseInt(rgbStr.substring(4, 6), 16);// WPS使用BGR順序return r + (g << 8) + (b << 16);}// 輔助函數:轉義正則表達式特殊字符function escapeRegExp(string) {return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');}

五、新增JS宏并配置菜單調用

在WPS中配置自定義功能區時:

(1)打開 "選項" > "自定義功能區"

(2)在 "從下列位置選擇命令" 下拉菜單中選擇 "宏"

(3)選擇 "Project.Module5.highlightCodeBlocks"

(4)將其添加到你自定義的功能區中

(5)為按鈕設置圖標和名稱

這樣配置后,點擊自定義功能區中的按鈕就能直接調用highlightCodeBlocks()函數,實現代碼塊高亮功能。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83720.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83720.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83720.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

el-tree結合el-tree-transfer實現穿梭框里展示樹形數據

參考文章&#xff1a;我把他的彈框單拉出來一個獨立文件作為組件方便使用&#xff0c;遇到一些問題記錄一下。 testComponet.vue <template><div class"per_container"><div class"per_con_left"><div class"per_con_title&q…

Go 后端中雙 token 的實現模板

下面是一個典型的 Go 后端雙 Token 認證機制 實現模板&#xff0c;使用 Gin 框架 JWT Redis&#xff0c;結構清晰、可拓展&#xff0c;適合實戰開發。 項目結構建議 /utils├── jwt.go // Access & Refresh token 的生成和解析├── claims.go // 從請求…

Typescript學習教程,從入門到精通,TypeScript 對象語法知識點及案例代碼(7)

TypeScript 對象語法知識點及案例代碼 TypeScript 是 JavaScript 的超集&#xff0c;提供了靜態類型檢查和其他增強功能。在 TypeScript 中&#xff0c;對象是面向對象編程&#xff08;OOP&#xff09;的基礎。 一、對象概述 在 TypeScript 中&#xff0c;對象是屬性的集合&a…

應用BERT-GCN跨模態情緒分析:貿易緩和與金價波動的AI歸因

本文運用AI量化分析框架&#xff0c;結合市場情緒因子、宏觀經濟指標及技術面信號&#xff0c;對黃金與美元指數的聯動關系進行解析&#xff0c;揭示本輪貴金屬回調的深層驅動因素。 周三&#xff0c;現貨黃金價格單日跌幅達2.1%&#xff0c;盤中觸及3167.94美元/盎司關鍵價位&…

命令行登錄 MySQL 報 Segmentation fault 故障解決

問題描述&#xff1a;對 mysql8.0.35 源碼進行 make&#xff0c;由于一開始因為yum源問題少安裝依賴庫 庫&#xff0c;在鏈接時遇到錯誤 undefined reference to&#xff0c;后來安裝了相關依賴庫&#xff0c;再次 make 成功。于是將 mysqld 啟動&#xff0c;再用 mysql -u roo…

Axure設計數字鄉村可視化大屏:構建鄉村數據全景圖

今天&#xff0c;讓我們一同深入了解由Axure設計的數字鄉村可視化大屏&#xff0c;看看它如何通過精心的布局和多樣化的圖表類型&#xff0c;將鄉村的各類數據以直觀、易懂的方式呈現出來&#xff0c;為鄉村管理者提供有力的數據支持。 原型效果預覽鏈接&#xff1a;Axure數字鄉…

3D個人簡歷網站 4.小島

1.模型素材 在Sketchfab上下載狐貍島模型&#xff0c;然后轉換為素材資源asset&#xff0c;嫌麻煩直接在網盤鏈接下載素材&#xff0c; Fox’s islandshttps://sketchfab.com/3d-models/foxs-islands-163b68e09fcc47618450150be7785907https://gltf.pmnd.rs/ 素材夸克網盤&a…

智能開發工具PhpStorm v2025.1——增強AI輔助編碼功能

PhpStorm是一個輕量級且便捷的PHP IDE&#xff0c;其旨在提高用戶效率&#xff0c;可深刻理解用戶的編碼&#xff0c;提供智能代碼補全&#xff0c;快速導航以及即時錯誤檢查。可隨時幫助用戶對其編碼進行調整&#xff0c;運行單元測試或者提供可視化debug功能。 立即獲取PhpS…

Spark 的運行模式(--master) 和 部署方式(--deploy-mode)

Spark 的 運行模式&#xff08;--master&#xff09; 和 部署方式&#xff08;--deploy-mode&#xff09;&#xff0c;兩者的核心區別在于 資源調度范圍 和 Driver 進程的位置。 一、核心概念對比 維度--master&#xff08;運行模式&#xff09;--deploy-mode&#xff08;部署…

sqli—labs第八關——布爾盲注

一&#xff1a;確定注入類型 按照我們之前的步驟來 輸入 ?id1 and 11-- ?id1 and 12-- 界面正常 第二行界面異常空白 所以注入類型為單引號閉合型 二&#xff1a; 布爾盲注 1.判斷是否使用條件 &#xff08;1&#xff09;&#xff1a;存在注入但不會直接顯示查詢結果 …

ARP 原理總結

&#x1f310; 一、ARP 原理總結 ARP&#xff08;Address Resolution Protocol&#xff09;是用于通過 IP 地址解析 MAC 地址的協議&#xff0c;工作在 鏈路層 與 網絡層之間&#xff08;OSI 模型的第三層與第二層之間&#xff09;。 &#x1f501; ARP通信過程&#xff1a; …

SpringCloud——EureKa

目錄 1.前言 1.微服務拆分及遠程調用 3.EureKa注冊中心 遠程調用的問題 eureka原理 搭建EureKaServer 服務注冊 服務發現 1.前言 分布式架構&#xff1a;根據業務功能對系統進行拆分&#xff0c;每個業務模塊作為獨立項目開發&#xff0c;稱為服務。 優點&#xff1a; 降…

機頂盒刷機筆記

疑難雜癥解決 hitool線刷網口不通tftp超時--》關閉防火墻cm201-2卡刷所有包提示失敗abort install--》找個卡刷包只刷fastboot分區再卡刷就能通過了&#xff08;cm201救磚包 (M8273版子&#xff09;&#xff09; 刷機工具 海兔燒錄工具HiTool-STB-5.3.12工具&#xff0c;需要…

Linux動靜態庫制作與原理

什么是庫 庫是寫好的現有的&#xff0c;成熟的&#xff0c;可以復用的代碼。現實中每個程序都要依賴很多基礎的底層庫&#xff0c;不可能每個人的代碼都從零開始&#xff0c;因此庫的存在意義非同尋常。 本質上來說庫是一種可執行代碼的二進制形式&#xff0c;可以被操作系統…

如何通過小智AI制作會說話的機器人玩具?

一、硬件準備與組裝 1. 核心硬件選擇 主控芯片&#xff1a;選擇支持無線網絡連接、音頻處理和可編程接口的嵌入式開發板 音頻模塊&#xff1a;配備拾音麥克風與小型揚聲器&#xff0c;確保語音輸入/輸出功能 顯示模塊&#xff1a;選擇適配的交互顯示屏用于可視化反饋 擴展模…

如何控制郵件發送頻率避免打擾用戶

一、用戶行為 監測用戶與郵件的互動數據&#xff0c;如打開率、點擊率下滑或退訂申請增多&#xff0c;可能是發送頻率過高的警示信號。利用郵件營銷平臺的分析工具&#xff0c;識別這些指標的變動趨勢&#xff0c;為調整提供依據。 二、行業特性與受眾差異 不同行業用戶對郵…

定積分的“偶倍奇零”性質及其使用條件

定積分的“偶倍奇零”性質是針對對稱區間上的奇偶函數積分的重要簡化方法。以下是其核心內容和應用要點&#xff1a; ?一、基本性質 ?偶函數&#xff08;偶倍&#xff09;? 若 f(x) 在 [?a,a] 上為偶函數&#xff08;即 f(?x)f(x)&#xff09;&#xff0c;則&#xff1a; …

如何在 Windows 11 或 10 上安裝 Fliqlo 時鐘屏保

了解如何在 Windows 11 或 10 上安裝 Fliqlo,為您的 PC 或筆記本電腦屏幕添加一個翻轉時鐘屏保以顯示時間。 Fliqlo 是一款適用于 Windows 和 macOS 平臺的免費時鐘屏保。它也適用于移動設備,但僅限于 iPhone 和 iPad。Fliqlo 的主要功能是在用戶不活動時在 PC 或筆記本電腦…

【C/C++】C++并發編程:std::async與std::thread深度對比

文章目錄 C并發編程&#xff1a;std::async與std::thread深度對比1 核心設計目的以及區別2 詳細對比分析3 代碼對比示例4 適用場景建議5 總結 C并發編程&#xff1a;std::async與std::thread深度對比 在 C 中&#xff0c;std::async 和 std::thread 都是用于并發編程的工具&am…

Axure疑難雜癥:垂直菜單展開與收回(4大核心問題與專家級解決方案)

親愛的小伙伴,在您瀏覽之前,煩請關注一下,在此深表感謝!如有幫助請訂閱專欄! Axure產品經理精品視頻課已登錄CSDN可點擊學習https://edu.csdn.net/course/detail/40420 課程主題:垂直菜單展開與收回 主要內容:超長菜單實現、展開與收回bug解釋、Axure9版本限制等問題解…