摘要:導語網上有很多自稱能實現移除注釋的正則表達式,實際上存在種種缺陷。為了避免正則的記憶功能,都使用了正則字面量進行測試。下面是去除單行注釋的最終代碼。修改方式將刪除注釋的正則改為。無法正確的移除引號塊。
導語
網上有很多自稱能實現移除JS注釋的正則表達式,實際上存在種種缺陷。這使人多少有些愕然,也不禁疑惑到:真的可以用正則實現嗎?而本篇文章以使用正則移除JS注釋為目標,通過實踐,由淺及深,遇到問題解決問題,一步步看看到底能否用正則實現!
移除注釋的完善思路:真的可以用正則實現? 1 單行注釋單行注釋要么占據一整行,要么處于某一行的最后。
正常情況下不難,直接通過正則匹配,再用replace方法移除便可。
let codes = ` let name = "Wmaker"; // This is name. if (name) { // Print name. console.log("His name is:", name); } `; console.log( codes.replace(///.*$/mg, "") ); // 打印出: // let name = "Wmaker"; // if (name) { // // console.log("His name is:", name); // }
上面是成功的刪除了注釋,不過對于獨占一整行的注釋清理的不夠徹底,會留下空白行。實際上,行尾注釋前面的空白也被保留了下來。所以目標稍稍提高,清除這些空白。操作起來也并不難,思路大致這樣:刪除整行,實際上是刪除本行末尾的換行符或上一行末尾的換行符。而換行符本身也屬于空白符。所以只需操作正則,匹配到注釋以及注釋前面所有的空白符即可,一箭雙雕。
let codes = ` let name = "Wmaker"; // This is name. if (name) { // Print name. console.log("His name is:", name); } `; console.log( codes.replace(/s*//.*$/mg, "") ); // 打印出: // let name = "Wmaker"; // if (name) { // console.log("His name is:", name); // }
如果在字符串中出現完整的URL地址,上面的正則會直接匹配而將其刪除。網上大多會將URL的格式特征(http://xxx):雙下劃線前面有冒號,作為解決途徑加以利用。但這只是治標不治本的做法,畢竟//以任何形式出現在字符串中是它的自由,我們無從干涉。
這樣問題就轉變成:如何使正則匹配存在于引號外的雙下劃線?
想匹配被引號包圍,帶有雙下劃線的代碼塊比較簡單:/".*//.*"/mg。難點在于如何實現這個否定,即當正則匹配到雙下劃線后,再判斷其是否在引號里面?絞盡腦汁,也上網查了很多,都沒有像樣的結果。靜心平氣,洗把臉刷刷牙再沖個頭冷靜之后,覺得單純使用正則的路已經走不通了,得跳出這個圈。
就在近乎精盡人亡的最后關頭,在那淫穢污濁的房間上方突然光芒萬丈。我急忙護住了充滿血絲的眼睛,靜待其適應后定睛一看。只見那里顯現出了一段文字(Chinese):孩兒啊,先將帶有//被引號包圍的字符串替換掉,去掉注釋后再還原,不就行了嗎?
let codes = ` let name = "Wmaker"; // This is name. if (name) { // Print name. console.log("His name is:", name); console.log("Unusual situation, characters of // in quotation marks."); } `; // 之前的方式。 console.log( codes.replace(/s*//.*$/mg, "") ); // 打印出: // let name = "Wmaker"; // if (name) { // console.log("His name is:", name); // console.log("Unusual situation, characters of // } // 現在的方式。 console.log( removeComments(codes) ); // 打印出: // let name = "Wmaker"; // if (name) { // console.log("His name is:", name); // console.log("Unusual situation, characters of // in quotation marks."); // } function removeComments(codes) { let {replacedCodes, matchedObj} = replaceQuotationMarksWithForwardSlash(codes); replacedCodes = replacedCodes.replace(/s*//.*$/mg, ""); Object.keys(matchedObj).forEach(k => { replacedCodes = replacedCodes.replace(k, matchedObj[k]); }); return replacedCodes; function replaceQuotationMarksWithForwardSlash(codes) { let matchedObj = {}; let replacedCodes = ""; let regQuotation = /".*//.*"/mg; let uniqueStr = "QUOTATIONMARKS" + Math.floor(Math.random()*10000); let index = 0; replacedCodes = codes.replace(regQuotation, function(match) { let s = uniqueStr + (index++); matchedObj[s] = match; return s; }); return { replacedCodes, matchedObj }; } }
是的,目標達成了,老天眷顧??!
另外,有一個需要優化的地方:定義字符串的方式有三種 " " ` ,目前我們只匹配了雙引號。
為了避免正則的記憶功能,都使用了正則字面量進行測試。 --- 之前 console.log( /".*//.*"/mg.test(`"Unu//sual"`) ); // false console.log( /".*//.*"/mg.test(`"Unu//sual"`) ); // true console.log( /".*//.*"/mg.test(``Unu//sual``) ); // false --- 之后 console.log( /("|"|`).*//.*1/mg.test(`"Unu//sual"`) ); // true console.log( /("|"|`).*//.*1/mg.test(`"Unu//sual"`) ); // true console.log( /("|"|`).*//.*1/mg.test(``Unu//sual``) ); // true
啊!問題到此結束了!
真的結束了嗎?不!我看了看時間:02:17,然后將眼鏡摘下,扯了張紙巾,拭去了幾顆淚水。
以下是接連解決的兩個問題:貪婪模式和轉義字符。
--- STEP 1,由于正則的貪婪模式導致。 let codes = ` let str = "abc//abc"; // abc" `; console.log( codes.match(/("|"|`).*//.*1/mg) ); // [""abc//abc"; // abc""] -- 解決 let codes = ` let str = "abc//abc"; // abc" `; console.log( codes.match(/("|"|`).*?//.*?1/mg) ); // [""abc//abc""] --- STEP 2,由定義字符串時其中的轉義字符導致。 let codes = ` let str = "http://x"x.com"; // "acs `; console.log( codes.match(/("|"|`).*?//.*?1/mg) ); // [""http://x"", ""; // ""] -- 解決 let reg = /(?事情到這里,雖然勞累,但多少有些成就感,畢竟成功了。
可是,可是,可是在測試時,竟然無意間發現一個無法逾越的障礙。就好比費勁千辛萬苦花費無盡的財力物力之后,某某尤物終于愿意一同去情人旅館時,卻發現家家爆滿,沒有空余的房間。在強裝歡笑,玩命的哄騙著她,一家接連一家的尋找直到終于定到房間后,卻發現自己已然挺不起來了!
正則會將任意位置的引號作為查找的起始位置,它不在乎引號是成雙的道理。下面是一個示例。
let reg = /(?不過,問題好歹在補過覺之后的 06:37 時得以解決。
思路是這樣的:雖然不能正確實現匹配帶有//被引號包圍的代碼塊(可能有方法,但能力有限),但是簡化成匹配單純被引號包圍的代碼塊,是簡單而且能正確做到的,雖然耗費的內存多了一些。另外,兩引號間也可能包含換行符,所以為其增加s模式:.代表全部字符。下面是去除單行注釋的最終代碼。let codes = ` let name = "Wmaker"; // This is name. let str = "http://x"x.com" + " / / " + "/"/"/"; // "; // " " if (name) { // Print name. console.log("His name is:", name); console.log("Unusual situation, characters of // in quotation marks."); } `; console.log(removeComments(codes)); // 打印出: // let name = "Wmaker"; // let str = "http://x"x.com" + " / / " + "/"/"/"; // if (name) { // console.log("His name is:", name); // console.log("Unusual situation, characters of // in quotation marks."); // } function removeComments(codes) { let {replacedCodes, matchedObj} = replaceQuotationMarksWithForwardSlash(codes); replacedCodes = replacedCodes.replace(/s*//.*$/mg, ""); Object.keys(matchedObj).forEach(k => { replacedCodes = replacedCodes.replace(k, matchedObj[k]); }); return replacedCodes; function replaceQuotationMarksWithForwardSlash(codes) { let matchedObj = {}; let replacedCodes = ""; let regQuotation = /(?最后補充一點,單雙引號雖然也可以多行顯示,但其解析后實際是單行的。
let codes = "" Wmaker ""; codes.match( /(? 2 多行注釋??!難點已經解決,現在就可以悠哉悠哉的往前推進了。
多行注釋與單行思路相同,只需在刪除注釋時多加一個匹配模式。中和兩者的最終代碼如下。let codes = ` let name = "Wmaker"; // This is name. let str = "http://x"x.com" + " / / " + "/"/"/"; // "; // " " let str = "http://x"x./*a*/com" + " / / " + "/"/"/"; // "; // "/*sad*/ " if (name) { // Print name. /* Print name. */ console.log("His name is:", name); console.log("Unusual situation, characters of // in quotation marks."); /* * Others test. */ console.log("Unusual situation, characters of /* abc */ in quotation marks."); } `; console.log(removeComments(codes)); // 打印出: // let name = "Wmaker"; // let str = "http://x"x.com" + " / / " + "/"/"/"; // let str = "http://x"x./*a*/com" + " / / " + "/"/"/"; // if (name) { // console.log("His name is:", name); // console.log("Unusual situation, characters of // in quotation marks."); // console.log("Unusual situation, characters of /* abc */ in quotation marks."); // } function removeComments(codes) { let {replacedCodes, matchedObj} = replaceQuotationMarksWithForwardSlash(codes); replacedCodes = replacedCodes.replace(/(s*//.*$)|(s*/*[sS]*?*/)/mg, ""); Object.keys(matchedObj).forEach(k => { replacedCodes = replacedCodes.replace(k, matchedObj[k]); }); return replacedCodes; function replaceQuotationMarksWithForwardSlash(codes) { let matchedObj = {}; let replacedCodes = ""; let regQuotation = /(? 3 總結從以上可以得出結論,單純使用正則表達式是不能達到目標的,需要配合其它操作才行。但現在得出的結果真的能覆蓋全部的情況?會不會有其它的隱藏問題,比如多字節字符的問題。雖然作為一個碼農,該有的自信不會少,但慢慢的也明白了自己的局限性。從網上的其它資料看,使用UglifyJS,或在正確的解析中去除注釋,會更為穩妥。但有可能自己動手解決的,沒理由不花費些精力試試!
問題更新記錄
已發現,暫時不能用此思路解決問題。
感謝熱心同志找出的錯誤,我會將能改與不能改的都列于此地,并只會更新下面兩個示例的代碼。1.沒有考慮正則字面量中的轉義字符。
出錯示例:var reg=/a//;。
修改方式:將刪除注釋的正則改為:/(s*(?。2.無法替換正則字面量。
出錯示例:var a=/abc/*123;var b=123*/123/。雖然的確是沒意義的代碼,但一無語法錯誤,二能被引擎解析。
修改方式:無,以之前的思維暫時沒辦法。
原因:無法像簡單替換引號一樣,先行替換正則字面量。3.無法正確的移除引號塊。
出錯示例:let codes = ` let name = "Wmaker"; // direct`ive of f" write as f". let name = "Wmaker"; // direct`ive of f" write as f". let name = ` /* name */ `; `;修改方式:無,以之前的思維暫時沒辦法。
原因:""的情況比較好解決,但是`即可單行也可多行。這里是工作于前端頁面的代碼及相應示例,下載鏈接。
Remove Comments 輸入:
輸出:
這里是工作于Node端的代碼及相應示例,下載鏈接。運行命令:node 執行文件 待轉譯文件 轉移后文件。
const fs = require("fs"); const path = require("path"); const process = require("process"); let sourceFile = process.argv[2]; let targetFile = process.argv[3]; if (!sourceFile || !targetFile) { throw new Error("Please set source file and target file."); } sourceFile = path.resolve(__dirname, sourceFile); targetFile = path.resolve(__dirname, targetFile); fs.readFile(sourceFile, "utf8", (err, data) => { if (err) throw err; fs.writeFile(targetFile, removeComments(data), "utf8", (err, data) => { if (err) throw err; console.log("Remove Comments Done!"); }); }); function removeComments(codes) { let {replacedCodes, matchedObj} = replaceQuotationMarksWithForwardSlash(codes); replacedCodes = replacedCodes.replace(/(s*(? { replacedCodes = replacedCodes.replace(k, matchedObj[k]); }); return replacedCodes; function replaceQuotationMarksWithForwardSlash(codes) { let matchedObj = {}; let replacedCodes = ""; let regQuotation = /(? 延伸閱讀ES6精華:字符串擴展。文章鏈接。
ES6精華:正則表達式擴展。文章鏈接。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/96056.html
摘要:在中就體現成用正則表達式進行文件名匹配。字符串匹配恩,那么不包括呢顯然,這就需要用到正則的位置匹配了和都是不正確的,匹配前面不是的的位置。參考正則表達式分鐘入門教程正則表達式匹配不包含某個字符串通俗易懂還有圖 問題重現 不知道各位旁友在webpack的使用中,有沒有碰到下面的問題情景: 在使用了css Module的情況下,同時又希望用一些global的布局,其實在css Module...
閱讀 1026·2021-10-19 11:42
閱讀 2981·2021-09-10 10:51
閱讀 689·2021-09-09 09:33
閱讀 1769·2021-09-01 10:43
閱讀 2779·2019-08-30 12:43
閱讀 3526·2019-08-30 11:24
閱讀 2131·2019-08-30 10:56
閱讀 2785·2019-08-29 11:00