用SketchUp替换文本内容

Apiglio

共 6899字,需浏览 14分钟

 · 2021-12-31


SketchUp 中的 ruby 控制台作为自动化操作的利器,自然也可以进行最基本的文档内容修改任务。因此本篇使用 SketchUp 自带的 ruby 控制台制作一个文本文档的替换小工具。


编程无疑是将自己从重复性工作中解放出来的好办法,但是对于完全的新手来说,某些编程工具在安装这一步就相当让人迷惑,以至于一开始就劝退很多人。就像在Windows中,谁都可以直接使用一些MS-DOS批处理指令或VBS脚本,对于安装了 SketchUp 的用户来说,ruby 控制台也是一个拿来即用的编程工具。或许 SketchUp 里的 ruby 只是一个简陋的版本,但这的确节省了不少在安装编程环境上的时间。


*这是“SketchUp不务正业系列”中的一篇,文中浅紫色文字为“不务正业”对“正业”的借鉴参考。


【本期目录】

一、替换文本的流程

二、正则表达式

三、代码实现

这是一个极其简单的功能,几乎完全不需要自己设计任务逻辑。




一、替换文本的流程


文本替换的流程是这样的:

①确定原文件的路径、替换文本的规则以及结果保存路径;

②读取文件内容;

③根据设置的规则替换内容;

④将替换后的内容保存到新的文件中。


新旧文件路径的选择可以使用最原始的输入地址的方法,但是对于文件路径比较复杂的情况来说,直接在窗口中选择文件还是要方便一些。因此这一步需要使用 SketchUp 提供的打开和保存文件的对话框:

filename=UI.openpanel("title")#>> filename为选择的文件名#   如果取消filename则为nil


执行以上代码可以打开系统默认的“打开”对话框,窗口的标题可以自己更改:


保存路径的选择也类似于“打开”对话框

filename=UI.savepanel("title")#>> filename为选择的文件名


二者的区别在于“保存”对话框可以设置新文件,且选择已有的文件会有覆盖警告;而“打开”对话框只能选择存在的文件。


确定了文件路径后是读取文件,可以使用以下代码:

str1=File.read(input_filename)#正常运行后str1将保存文件中的所有内容#类型为String


将字符串保存到新的文件中,则需要使用如下的代码:

f=File.open(filename,"w")f.write(str2)f.close()


以上代码中:第1行 .open 方法表示创建文件,其中的第二个参数 "w" 表示允许覆盖创建。第2行的 .write 方法表示将 str2 写入文件中,第3行的 .close 方法保存文件。


最后是文本替换部分,这本是一个很复杂的任务逻辑,但是ruby有自带的方法能够实现这个功能,唯一要做的就是了解字符串类的 .gsub 方法:

str2 = str1.gsub(op,np)


简单来说,这个方法就是以参数规定的规则替换 str1 中的特定内容,然后将结果返回给 str2。如果两个参数都是字符串,那么规则就是将文本中所有 op 替换成 np。如果参数 op 是正则表达式,那么替换的规则将更加复杂且灵活。


二、正则表达式


关于正则表达式,这里只提供一些感性的例子,详细的语法规则和使用方法不是本篇文章能够概括的。可以参考以下两个网址:

  • https://ruby-doc.org/core-2.7.1/String.html#method-i-gsub

  • https://www.runoob.com/regexp/regexp-tutorial.html


(1) 简易替换


这种用法和系统自带的记事本替换功能没有区别,但是需要规避某些有特殊含义的符号:

图中的 "\xe7\x94\xa8\xe5\x9c\xb0" 表示“用地”,这么写是因为 ruby 控制台的输入不是 UTF-8 编码,所以会出现编码错误。如果需要使用中文则需要使用 load 方法调用写好的 *.rb 文件


(2) 编号识别替换


对于有明确规则的编号,正则表达式可以找出这些编号并加以利用。

s1="aapid=000022 aibuebcfw@#RD# pid=002014 ksheFW$# pid=B36A24"s1.gsub(/pid=([0-9,A-F]{6})/,'^([\1])')#>> aa^([000022]) aibuebcfw@#RD# ^([002014]) ksheFW$# ^([B36A24])

以上代码中的正则表达式 /pid=([0-9,A-F]{6})/ 含义为:以 “pid=” 开始包含 6 位十六进制数位([0-9,A-F])的片段, {6} 表示前一个方括号表示的字符的重复次数为 6。圆括号则用于替换,第一个圆括号所包含的匹配字段用 \1 表示,如果有更多圆括号,则以此类推使用 \2、 \3、 \4……所以最终的效果便是将诸如 “pid=000022” 的片段替换为 “^([000022])”。


另外需要注意,例子中第二个参数使用了单引号,双引号则需要写成 "^([\\1])" ,因为双引号字符串中的反斜杠会被当做转义符号解读。


(3) 断行处理


断行是文本文档与表格文件对接的重要字符,找到符合条件的断点非常关键:

s2=" nah 1200.0 9h lazarus 3235.1 6h requiem 1023.5 7h"s2.gsub(/(\s[0-9]+h)/,'\1'+"\r\n")#>> nah 1200.0 9h#>> lazarus 3235.1 6h#>> requiem 1023.5 7h

以上代码中的 '\1'+"\r\n" 使用了两种引号就是为了在适当的时候使用转义符号,也可以直接使用 "\\1\r\n" 这样的表达。表达式 /(\s[0-9]+h)/ 表示以空格符号(\s)开始以字符 “h” 结尾,中间有一个或多个(+)数字 0-9 的文本片段。


(4) 调换顺序


还可以根据具体要求调换局部文本的顺序:

s3="27.3324N 116.2132E 42.3324N 102.2132E 10.9323E 79.2214W"s3.gsub(/([0-9\.]+)([NEWS])/,'\2=\1')#>> N=27.3324 E=116.2132 N=42.3324 E=102.2132 E=10.9323 W=79.2214

以上代码中正则表达式包含两个圆括号,因此替换字符串可以同时引用 \1\2。表达式 /([0-9\.]+)([NEWS])/ 表示以一个或多个(+)数字 0-9 开始,后跟随小数点(\.),而后以 NEWS 的其中一个字符结尾的文本片段。


(5) 数字识别处理


使用正则表达式还可以很方便的提取出数字文本:

s4="1200.0 1212.843 3235.1 4413.31 1023.5 "s4.gsub(/\.([0-9]{,2})\s/,'.\10 ').gsub(/\.([0-9]{,2})\s/,'.\10 ')#>> 1200.000 1212.843 3235.100 4413.310 1023.500
s5="lingling 40 hours 3424"s5.gsub(/([0-9]+)(\shours)/,'\1 minutes')#>> lingling 40 minutes 3424

以上代码第2行通过连续两次 .gsub 方法将小数点后位数追加到三位。表达式 /\.([0-9]{0,2})\s/ 表示的是以小数点(\.)开始以空格符号(\s)结尾,且中间包含不多于2个({,2})的数字 0-9,识别小数部分后在最后追加一个 “0”,以达到补足小数位数的效果。其中的 {,2} 表示前一个方括号代表的字符可以重复2次及以下。第6行代码则识别具体的数量单位并替换。


(6) 单词替换


正则表达式也可以可以根据单词长度或者其他规则自制填空题:

s6="A String object holds and manipulates an arbitrary sequence "  +"of bytes, typically representing characters. String objects "  +"may be created using ::new or as literals."#》 s6.gsub(/::[^\s]+/,'___')#>> A String object holds and manipulates an arbitrary sequence #>> of bytes, typically representing characters. String objects #>> may be created using ___ or as literals.

文中的表达式 /::[^\s]+/ 表示以 “::” 开头到空格符号截止的一段文本。其中的 [^\s] 表示除了空格符号(\s)以外的所有符号,符号 “+” 表示一个或更多,相当于 {1,} 的表达。


(7) 序列


甚至可以对长文本序列进行特定数位的截断:

s6="please call 01189998819991197253"s6.gsub(/([0-9]{3})/,'\1-')#>> please call 011-899-988-199-911-972-53

根据正则表达式的匹配原则,符合条件的文本中的部分片段不会再重新匹配,因此可以实现每隔固定的位数插入其他字符。


三、代码实现


最后将整个过程封装在 ApiglioToolBox 模块中,由于ruby控制台不支持UTF8输入,所以直接复制进控制台时不能使用带汉字的代码,所以这里直接使用了转义符号:

module ApiglioToolBox  def self.file_rep_func(input,output,old,new)    str1=File.read(input)    str2=str1.gsub(old,new)    f=File.open(output,"w")    f.write(str2)    f.close()  end  def self.file_rep()    open_tip="\xe9\x80\x89\xe6\x8b\xa9\xe9\x9c\x80\xe8\xa6\x81\xe6\x9b\xbf\xe6\x8d\xa2\xe5\x86\x85\xe5\xae\xb9\xe7\x9a\x84\xe6\x96\x87\xe6\xa1\xa3"    #选择需要替换内容的文档    save_tip="\xe6\x9b\xbf\xe6\x8d\xa2\xe7\xbb\x93\xe6\x9e\x9c\xe4\xbf\x9d\xe5\xad\x98\xe4\xb8\xba"    #替换结果保存为    inpu_tip="\xe6\x9b\xbf\xe6\x8d\xa2\xe9\x80\x89\xe9\xa1\xb9\xef\xbc\x88\xe6\xad\xa3\xe5\x88\x99\xe8\xa1\xa8\xe8\xbe\xbe\xe5\xbc\x8f\xef\xbc\x89"    #替换选项(正则表达式)    op="\xe6\x9f\xa5\xe6\x89\xbe\xef\xbc\x9a"    #查找:    np="\xe6\x9b\xbf\xe6\x8d\xa2\xe4\xb8\xba\xef\xbc\x9a"    #替换为:    mp="\xe6\xa8\xa1\xe5\xbc\x8f\xef\xbc\x9a"    #模式:    mo_ord="\xe6\x99\xae\xe9\x80\x9a\xe6\xa8\xa1\xe5\xbc\x8f"#普通模式    mo_reg="\xe6\xad\xa3\xe5\x88\x99\xe8\xa1\xa8\xe8\xbe\xbe\xe5\xbc\x8f"#正则表达式        raise RuntimeError.new("Dialog Cancelled") unless filename=UI.openpanel("FileReplace: "+open_tip)    raise RuntimeError.new("Dialog Cancelled") unless targetname=UI.savepanel("FileReplace: "+save_tip)    patterns=UI.inputbox([op,np,mp],["","",mo_ord],["","",mo_ord+"|"+mo_reg],inpu_tip)    raise RuntimeError.new("Dialog Cancelled") unless patterns    return false if patterns[0]==""    case patterns[2]    when mo_reg      patterns[0]=patterns[0]+"/" if patterns[0][-1]!="/"      patterns[0]="/"+patterns[0] if patterns[0][0]!="/"      puts "ApiglioToolBox.file_rep_func('"+filename+"','"+targetname+"',"+patterns[0]+",'"+patterns[1]+"')"      patterns[0]=eval(patterns[0])    when mo_ord      puts "ApiglioToolBox.file_rep_func('"+filename+"','"+targetname+"',\""+patterns[0]+"\",\""+patterns[1]+"\")"    end    file_rep_func(filename,targetname,*patterns[0..1])    true  endend


使用这个替换工具只需要在控制台输入:

ApiglioToolBox.file_rep

而后只需要根据弹出的对话框进行相应的设置即可。当然弹出对话框也未必总是那么方便,因此还可以通过调用 .file_rep_func 方法,通过给定参数来避免在对话框中设置参数:

ApiglioToolBox.file_rep_func(input,output,old_pattern,new_pattern)#input       输入的文本文档#output      输出的文本文档#old_pattern 需要替换的内容(用双引号包括)#            需要替换内容的正则表达式(用斜杠包括)#new_pattern 替换后的内容(如不想使用转义符号则使用单引号)#            替换内容可以增加\1 \2 \3 来引用匹配到的文本


以下是文本替换工具的使用效果:


替换前后文件的内容如下:



 正业  这是一个与建模完全无关的小工具,只是利用 SketchUp 中的 ruby 控制台的图灵完备进行一些简易的编程。其存在的意义在于:很多自动化任务只需要非常简单的技术手段,但是为此 beginner 却需要安装整套的语言环境,这是相当繁琐甚至没有必要的。ruby 控制台完全省去了安装开发环境的门槛,不需要额外进行任何准备,再加上 ruby 本身的语法特点,使得这类小功能可以很轻松很愉悦的实现,对于本就安装了 SketchUp 的用户来说十分便利。


另外,这个工具使用了 UI 模块中的几个对话框方法(其实就是系统的对话框)来串联整个工具的逻辑,甚至可以给它设置一个 Toolbar 使之变成一个按键,成为典型意义上的插件功能。但是这种连续跳好几个对话框的处理方式还是太过冗杂,更合适的做法是使用 UI:: HtmlDialog 做类似于ArcToolBox 的窗口,UI 逻辑会明显清晰得多。正如本系列创作的初衷,虽然名为“不务正业”,但怪异旁门的方法也能够给“正统”的使用方式以参考借鉴。


都看到这了,点个赞呗)



本篇文章是“SketchUp不务正业系列”的其中一篇,编号为SU-2021-S02,更多与 SketchUp Ruby有关的其他文章可以点击公众号菜单中的“SU Ruby”选项获得文章的目录。


浏览 212
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报