EmEditor的\J模式

EmEditor处理文本非常强大,它的替换框还可以用js操作,也就是\J模式,这势必让EmEditor如虎添翼。但官方帮助语焉不详,只有如下解说与示例:

指定表达式使用 JavaScript。\J 必须放在替换表达式的开头。可以与反向引用结合使用。还可以在脚本中使用 cell 函数。例如,

替换表达式 含义
\J”\0”+”abc” 合并匹配字符串与”abc”
\J”\0”.substr(0,5); 返回匹配字符串的前5个字符
\J\0*100; 将匹配的数字乘以100
\JparseFloat(\0).toFixed(2); 将匹配的数字四舍五入到小数点后2位
\Jcell(-1) 返回位于匹配单元格左侧相邻单元格中的文本
\JparseFloat(cell(-1))
+parseFloat(cell(-2)) 返回左侧两个相邻单元格的总和

验证示例可以得出的结论是:
一、\J模式可以像一般的正则替换一样反向引用,虽然例子中只有\0,其它如\1等也适用。
二、该模式中的反向引用如果是字符串,要加引号;如果是数字,不加引号。
三、在csv模式下可以使用cell函数,参数0指查找框可以匹配的内容所在的单元格,-n指该单元左侧第n个单元格,n指右侧第n个单元格。
四、不同的数据类型可以调用js中相应的函数操作数据。
五、按下替换按钮时,会把适配查找框的内容替换为js中最后一个表达式的值。

但对这种混合模式还有不少疑问,尤其在替换框中用replace会是什么情况,我做了如下测试(没有耐心的朋友可以直接看结论)。

测试数据:m.20101029-m300-w001-003.001195 → 一砣

1.查找:w(\d+)-(\d+);替换:\J ‘\1’.replace(‘\1’,‘\2’);结果:m.20101029-m300-003.001195 → 一砣;即:w001-003被替换为003;也即:查找框的匹配部分被替换为\2的内容。解释:先是js查找\1即001中的\1(001),然后把它替换为\2(003),这时函数replace返回值为003,按替换按钮,查找框的内容(w001-003)被替换为函数值003。

2.查找:w(\d+)-(\d+);替换:\J ‘\0’.replace(‘\1’,‘\2’);结果:m.20101029-m300-w003-003.001195 → 一砣 ;即:w001-003被替换为w003-003;也即:查找框的匹配内容只有\1(001)被替换为\2(003);解释:先是js查找\0即w001-003中的\1(001),然后把它替换为\2(003),这时函数replace返回值为w003-003,按替换按钮,查找框的内容(w001-003)被替换为函数值w003-003。

3.查找:w(\d+)-(\d+);替换:\J ‘\0’.replace(‘\2’,‘大小’);结果:m.20101029-m300-w001-大小.001195 → 一砣;也即:查找框的匹配内容只有\2(003)被替换为“大小”;解释:先是js查找\0即w001-003中的\2(003),然后把它替换为“大小”,这时函数replace返回值为w001-大小,按替换按钮,查找框的内容(w001-003)被替换为函数值w001-大小。

4.查找:w(\d+)-(\d+);替换:\J ‘\1’.replace(‘\2’,‘大小’);结果:m.20101029-m300-001.001195 → 一砣 ;也即:查找框的匹配内容只有\1(001)被保留,其它内容都被删除或替换为“”;解释:先是js查找\1中的\2(003),但\1中并没有003,函数replace就没有进行替换操作,这时函数replace返回值为\1(001),按替换按钮,查找框的内容(w001-003)被替换为函数值001。
5.查找:w(\d+)-(\d+);替换:\J ‘\0’.replace(‘一砣’,‘大小’);结果:m.20101029-m300-w001-003.001195 → 一砣 也即:查找框的匹配内容被完全保留;解释:先是js查找\0(w001-003)中的“一砣”,但\0中并没有“一砣”,函数replace就没有进行替换操作,这时函数replace返回值为\0(w001-003),按替换按钮,查找框的内容\0(w001-003)被替换为函数值w001-003。

6.查找:w(\d+)-(\d+);替换:\J ‘\0’.replace(‘0’,‘大’);结果:m.20101029-m300-w大01-003.001195 → 一砣 ;即:w001-003被替换为w大01-003;也即:查找框的匹配内容只有w后的0被替换为“大”;解释:按道理好像应该替换为w大大1-大大3呀(python思维)。但可能是先是js查找\0即w001-003中的0,第一次查找时把w后的0替换为“大”,这时函数replace的调用字符串\0在内容中已被替换为w大01-003,已经与查找内容\0(w001-003)不一致,所以em强制replace停止替换,因为js不加全局标志的replace只替换一次(后补),返回值就停留在w大01-003,按替换按钮,查找框的内容(w001-003)被替换为函数值w大01-003。
7.查找:w(\d+)-(\d+);替换:\J ‘\1’.replace(‘0’,‘大’);结果:m.20101029-m300-大01.001195 → 一砣;即:w001-003被替换为大01;解释:可能是先是js查找\1即001的0,第一次查找时把第一个0替换为“大”,这时函数replace的调用字符串\1在内容中已被替换为“大01”,已经与查找内容\1(001)不一致,所以em强制replace停止替换,返回值就停留在“大01”,按替换按钮,查找框的内容(w001-003)被替换为函数值“大01”。
8.查找:^.+?w(\d+)-(\d+).+$;替换:\J ‘\1’.replace(‘0’,‘大’);结果:大01;解释:js查找\1(001)中的0,第一次查找时把第一个0替换为“大”,这时函数replace的调用字符串\1在内容中已被替换,已经与查找内容\1不一致,所以em强制replace停止替换,返回值就停留在第一个0被替换状态,即““大01””,按替换按钮,查找框的内容\0被替换为函数值(“大01”)。
9.查找:^.+?w(\d+)-(\d+).+$;替换:\J ‘\1’.replace(/(0)(1)/,‘9$1’);结果:090;解释:js查找\1(001)中的01,然后把它们替换为9和replace中的第一个括号中的内容(0),这时replacer的返回值是090,按替换按钮,查找框的内容\0被替换为函数值(“090”)。
10.查找:^.+?w(\d+)-(\d+).+$;替换:\J if(‘\1’){‘\0’.replace(/0/g,‘\5’)};结果:m.21129-m3-w1-3.1195 → 一砣 ;即:匹配查找框的部分中的所有0都被删除;解释:这里加了个条件判断,因为反向引用\1存在,条件为真。replace查找部分加上了全局标志,而反向引用\5不存在,em把它当作空字符替换所有的0,然后返回值。
11.查找:^.+?w(\d+)-(\d+).+$;替换:\J if(‘\5’){‘\0’.replace(/0/g,‘大’)};结果:undefined ;;解释:这里也有条件判断,但因为反向引用\5不存在,条件为假,后面的语句不会执行,这样em的替换框就没有返回值,但又不等于空字符。

总结:
一、替换框中的js代码包括replace并不能替换em查找框中的内容,只是以其返回值作为em的替换内容;
二、按替换按钮后,替换内容替换的是查找框匹配的所有内容,即\0;
三、replace在查找替换时,如果在指定字符串中查找不到,返回值就是指定字符串;
四、replace在查找替换时,如果能查找到,不加g标志只替换一次,加g标志会替换所有,然后返回相应值;
五、\1 \2之类无论在replace的查找还是替换位置都指的是em查找框中的相应内容,要在replace替换部分引用repalce查找部分括号中的内容,要用$1 $之类。
六、在js中如果反向引用不存在,会被视为空字符。

结论:在替换框中用replace往往与所想效果不同,坑很多,不搞清楚机理,就会出现莫名其妙的问题。

提醒:em中的\J模式使用 Chakra(相当于 Microsoft Edge Legacy),并且最多支持到 ECMAScript 5.1。但\V模式可能还有bug,更要慎用。

感谢:以上问题是受791631671群中大佬的启发,群中许多大佬非常热情,不胜感激。

7 Likes