Project1

标题: 如何匹配字符串中的成对括号? [打印本页]

作者: taroxd    时间: 2014-6-20 18:06
标题: 如何匹配字符串中的成对括号?
本帖最后由 taroxd 于 2014-7-21 20:32 编辑

技术讨论区向来是脚本大触的后花园,于是我来打破这一现状,来问脚本新手问题啦~

我想要在事件-显示文字等地方实现 #{} 这种类似 ruby 内嵌表达式的功能。
而喵呜喵的版本中,至少有两个美中不足之处。
1. 当你真的需要输入#{}时无法输入
2. 表达式不能内嵌花括号{}

问题 1 很容易解决,和ruby的双引号字符串一样,可以通过 \#{} 来实现
而问题 2 就是我想请教的。如何较为简洁地实现括号配对,进行匹配的功能?

我正则表达式学艺不精,因此只尝试了最原始的循环方式。
以下是我尝试的,不美观而且难以理解的代码(发布于此)。
其中 old 是原方法的返回值,也就是要进行处理的字符串。
Taroxd::Script.process 方法是对代码做一些处理,定义在上面的链接中,与这个问题无关。
代码


用ruby写出这种代码毕竟是非常难受的事情,而且匹配成对括号的功能我以后可能还会再次用到。因此我来请教,有什么更好的解决方式没有?
而且,我的方法还有个问题,比如说 #{'}'} 就会发生错误,而正确的解释为 "}"

感谢23L的解答,现在的代码是
代码

作者: fux2    时间: 2014-6-20 18:50
默认贪婪匹配应该没什么问题吧,我暂时没想到什么问题。
RUBY 代码复制
  1. \([^\(\)]*\)

大概能想到的只有这样,匹配一层括号中的内容,循环匹配之后清除掉就好了。
作者: taroxd    时间: 2014-6-20 19:01
fux2 发表于 2014-6-20 18:50
默认贪婪匹配应该没什么问题吧,我暂时没想到什么问题。
\([^\(\)]*\)[/pre]
大概能想到的只有这样 ...

清除掉之后怎么装回去?

因为我需要最外层括号内的完整内容
如:(1)) 匹配1
      ((1) 匹配失败
      ((1)) 匹配(1)
作者: fux2    时间: 2014-6-20 19:03
taroxd 发表于 2014-6-20 19:01
清除掉之后怎么装回去?

因为我需要最外层括号内的完整内容

你把字符串克隆一份再进行破坏性操作嘛。
作者: taroxd    时间: 2014-6-20 19:04
本帖最后由 taroxd 于 2014-6-20 19:07 编辑
fux2 发表于 2014-6-20 19:03
你把字符串克隆一份再进行破坏性操作嘛。


求演示

测试字符串 #{}123#{44+1}67#{}89\#{}\\#{{sym: true}}
结果 123456789#{}\{:sym=>true}

克隆完之后,匹配原字符串只能得到括号起始的位置。而这个位置其实不需要层层匹配也可以直接获得的吧?
作者: fux2    时间: 2014-6-20 19:11
taroxd 发表于 2014-6-20 19:04
求演示

测试字符串 #{}123#{44+1}67#{}89\#{}\\#{{sym: true}}

我以为你说的是正则匹配括号,看来跑题了.
无论怎么样也不可能得到这样的结果吧,你的表达式本来就是错误的。
作者: taroxd    时间: 2014-6-20 19:22
fux2 发表于 2014-6-20 19:11
我以为你说的是正则匹配括号,看来跑题了.
无论怎么样也不可能得到这样的结果吧,你的表达式本来就是错误 ...

{sym: true} 是 Ruby1.9 之后的一个哈希的语法啦
等价于 {:sym => true},这玩意儿 to_s 当然就是 {:sym=>true}
作者: 余烬之中    时间: 2014-6-20 19:22
打算尝试一下【匹配成对括号】这个方法本身
参数:待匹配字符串
返回值:括号内内容的数组?
作者: taroxd    时间: 2014-6-20 19:24
余烬之中 发表于 2014-6-20 19:22
打算尝试一下【匹配成对括号】这个方法本身
参数:待匹配字符串
返回值:括号内内容的数组? ...

由于我是要实现 #{} 这种类似 ruby 内嵌表达式的功能

因此只需要最大的一个花括号里的内容就行了

#{{} ← 没有匹配
#{}} ← 匹配 #{}
#{{}} ← 匹配 #{{}}

我的原始方法实现至少是可以完成这个功能的……
作者: 余烬之中    时间: 2014-6-20 19:37
本帖最后由 余烬之中 于 2014-6-20 19:38 编辑
taroxd 发表于 2014-6-20 19:24
由于我是要实现 #{} 这种类似 ruby 内嵌表达式的功能

因此只需要最大的一个花括号里的内容就行了


只需要最外层?直接贪婪不久好了?

或者是这样:"文字#{待处理内容}文字#{另一些待处理内容}"

另外你的例子我看不懂 是不是这样:

#{{} ← 没有匹配
#{}} ← 匹配 }
#{{}} ← 匹配 {}
作者: taroxd    时间: 2014-6-20 19:42
本帖最后由 taroxd 于 2014-6-20 19:47 编辑
余烬之中 发表于 2014-6-20 19:37
只需要最外层?直接贪婪不久好了?

或者是这样:"文字#{待处理内容}文字#{另一些待处理内容}"


不是。我需要的是成对括号的最外层

当我们用双引号字符串 "#{}1}" 的时候,结果显然是 "1}" 而不是 "}1"
而 "#{{}"则会直接报错

举这个例子你明白了吗?
作者: 余烬之中    时间: 2014-6-20 19:51
taroxd 发表于 2014-6-20 19:42
不是。我需要的是成对括号的最外层

当我们用双引号字符串 "#{}1}" 的时候,结果显然是 "1}" 而不是 "}1" ...

这样?
  "#{待处理}文本}"
  我们需要获得待处理进行处理 然后连接文本

这样我不建议不区分不同功能的括号的书面写法

当然 我打算写的和具体使用的有差别 只针对【匹配字符串中的成对括号,并且筛选内容】
作者: taroxd    时间: 2014-6-20 19:53
余烬之中 发表于 2014-6-20 19:51
这样?
  "#{待处理}文本}"
  我们需要获得待处理进行处理 然后连接文本

就是要这个效果,我在代码中实现的也就是这个效果

有时待处理可能会含有大括号,就像字符串中有时也会遇到双引号一样
作者: 余烬之中    时间: 2014-6-20 20:01
taroxd 发表于 2014-6-20 19:53
就是要这个效果,我在代码中实现的也就是这个效果

有时待处理可能会含有大括号,就像字符串中有时也会遇 ...

有时待处理可能会含有大括号
可是这样的话 如何区分呢?
情景A
  "#{待}处{理}文本}"
情景B
  "#{待处理}文{}本}"

要如何区分大括号到底是不是待处理的内容
(直接不匹配?)
作者: taroxd    时间: 2014-6-20 20:10
余烬之中 发表于 2014-6-20 20:01
有时待处理可能会含有大括号
可是这样的话 如何区分呢?
情景A


情景A
  "#{}处{理}文本}"
情景B
  "#{待处理}文{}本}"

你直接拿Ruby试一下就好了
作者: moy    时间: 2014-6-20 20:58
我就想知道你们怎么做到 "#{吧唧吧唧}" 而不报错的……正常来说不是直接说没这变量吗。
作者: taroxd    时间: 2014-6-20 21:03
本帖最后由 taroxd 于 2014-6-20 21:06 编辑
moy 发表于 2014-6-20 20:58
我就想知道你们怎么做到 "#{吧唧吧唧}" 而不报错的……正常来说不是直接说没这变量吗。 ...


不懂你在说什么

我只知道有了这货我可以做许多死,比如道具的描述随队伍而变化,比如在显示文字里面直插某个计算出的数值之类的

https://rpg.blue/thread-365976-1-1.html 这里可是有各种奇怪的功能的

写个啥 #{S[3] ? 1 : 2} 之类的多简洁清晰~~~
作者: 余烬之中    时间: 2014-6-20 21:05
本帖最后由 余烬之中 于 2014-6-20 21:29 编辑

就这样 完成了一个方法
方法不能直接使用 满足你的适用范围的话 必须先处理字符串 将不需要处理的括号转义 匹配后再转回
正准备发给你的时候发现只能处理单字节字符 不过现在已经可以处理双字节字符了
方法说明
嘿!代码!

作者: moy    时间: 2014-6-20 21:07
taroxd 发表于 2014-6-20 21:03
不懂你在说什么

我只知道有了这货我可以做许多死,比如道具的描述随队伍而变化,比如在显示文字里面直插 ...
  1. a = "#{xyz}"
  2. #=> no methods or variables name“xyz”
复制代码
……我也没弄明白
作者: taroxd    时间: 2014-6-20 21:10
moy 发表于 2014-6-20 21:07
……我也没弄明白

https://rpg.blue/thread-365976-1-1.html
最上端的脚本提供了 V, S, A 之类“常量”的定义

实在不行还能读取全局变量的嘛~
作者: taroxd    时间: 2014-6-20 21:14
本帖最后由 taroxd 于 2014-6-20 21:33 编辑
余烬之中 发表于 2014-6-20 21:05
就这样 完成了一个方法
方法不能直接使用 满足你的适用范围的话 必须先处理字符串 将不需要处理的括号转义  ...


很感谢你的方法……让我参考一下……不过我要的还有替换功能啊233
而且用你的方法,我无法判断那些属于“最外层”。因为你的输出结果是从左至右,而不是由外到内。当然这一点是可以通过 valid.size == 1 来进行判断的
如果我不是需要 i 来表示位置的话,大概也不会比你的方法繁琐吧。
总之虽然我用不到,还是很感谢

另外继续为难一下: '#{\'}\'}'(显示为#{'}'}),替换为}
(这是ruby的双引号中可以做到的)

反正我是没成功
作者: 余烬之中    时间: 2014-6-20 21:44
本帖最后由 余烬之中 于 2014-6-20 21:59 编辑
taroxd 发表于 2014-6-20 21:14
很感谢你的方法……让我参考一下……不过我要的还有替换功能啊233
而且用你的方法,我无法判断那些属于“ ...


"#{'}'}" #=> "}"
'#{\'}\'}' #=> "\#{'}'}" # 期望-> "}"
'#{"Foo" + \'} + "Bar"\'}' #=> "\#{\"Foo\" + '}' + \"Bar\"}" # 期望-> "Foo}Bar"

是这样吗

---------
一开始忘了each_char 用了each_byte 结果不能处理双字节 最后还是用了clone+slice!  然后突然想到了这东西 一下子ruby了
作者: 晴兰    时间: 2014-6-20 22:02
提示: 作者被禁止或删除 内容自动屏蔽
作者: 余烬之中    时间: 2014-6-20 22:05
本帖最后由 余烬之中 于 2014-6-20 22:22 编辑
taroxd 发表于 2014-6-20 21:14
很感谢你的方法……让我参考一下……不过我要的还有替换功能啊233
而且用你的方法,我无法判断那些属于“ ...


FUCK 我居然没有想到
->{puts '}'}
这个用我的方法会匹配错误

不对 我被绕进去了
我这个方法的目的只是分组匹配
在这个范围内使用毫无问题

顺便 楼上正则式的链接 http://rubular.com/r/KB9DX2hV4U

==========================

改了一下result的数据结构 现在是二维数组了 第一个表示字符 第二个表示深度 以最外层没有括号的为0深度
RUBY 代码复制
  1. def mBrackets str, brackets = {'(' => ')', '[' => ']', '{' => '}', '<' => '>'}
  2.   matched = []
  3.   result = [["", 0]]
  4.   valid = [0]
  5.   str.clone.each_char do |c|
  6.     valid.each{|v| result[v][0].concat(c)}
  7.     if brackets.keys.include?(c)
  8.       matched.push(c)
  9.       valid.push(result.size)
  10.       result.push(["", valid.size - 1])
  11.     elsif brackets.values.include?(c)
  12.       if brackets[matched[-1]] == c
  13.         result[valid[-1]][0].chop!
  14.         valid.pop
  15.         matched.pop
  16.       else
  17.         raise ArgumentError, "False Matching!"
  18.       end
  19.     end
  20.   end
  21.   result
  22. end

作者: taroxd    时间: 2014-6-21 05:55
本帖最后由 taroxd 于 2014-6-21 07:36 编辑
晴兰 发表于 2014-6-20 22:02
Ruby的正则可能没有其他语言的正则平衡组,但有一个叫做递归匹配的语法,大概的含义就是
mygroup应该匹配{m ...


在Ruby文档里找到了 Subexpression Calls,这个功能似乎就是专为这个时候设计的

十分感谢,问题解决了。如果真要追究 #{'}'} 的话就得语法分析了吧

作者: taroxd    时间: 2014-6-21 06:27
本帖最后由 taroxd 于 2014-6-21 06:56 编辑
晴兰 发表于 2014-6-20 22:02
Ruby的正则可能没有其他语言的正则平衡组,但有一个叫做递归匹配的语法,大概的含义就是
mygroup应该匹配{m ...


那么我再问一个问题,如何在gsub中引用a?当然这个问题在这里其实不重要,但我希望能给之后留作参考。
作者: 无脑之人    时间: 2014-6-25 12:35
你们真是可怕,咱都是本着「不按照咱的要求来写都得死」的原则来做的【噗
反正是自己匹配,不如直接做一个DSL算了,何必迎合Ruby的要求呢




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1