ruby-on-rails 从已转换为String的Array中提取符号?

pvabu6sv  于 12个月前  发布在  Ruby
关注(0)|答案(2)|浏览(132)

我有以下字符串:
s = "[:test1, :test2]"
我希望它再次以一个符号数组结束:
[:test1, :test2]
我发现了两个类似的问题:
Ruby: Parsing a string representation of nested arrays into an Array?
Ruby convert string array to array object
但是JSON.parse和YAML.load都不起作用:

irb(main):043> test
=> "[:test1, :test2]"
irb(main):044> JSON.parse test
/Users/[...]/.rbenv/versions/3.2.2/lib/ruby/3.2.0/json/common.rb:216:in `parse':
unexpected token at ':test1, :test2]' (JSON::ParserError)
irb(main):045>
irb(main):045> YAML.load test
/Users/[...]/.rbenv/versions/3.2.2/gemsets/rails_test/gems/psych-5.1.1.1/lib/psych/parser.rb:62:in `_native_parse': (<unknown>):
did not find expected node content while parsing a flow node at line 1 column 2 (Psych::SyntaxError)

eval确实可以工作,但由于注入问题,通常不推荐使用。

irb(main):047> eval(test)
=> [:test1, :test2]

我能找到的唯一方法是一些基本的字符串操作,但我想知道是否有一个更好的方法内置到Ruby中,不涉及创建一个自定义函数。

irb(main):046> test.delete("[]:").split(", ").map(&:to_sym)
=> [:test1, :test2]

Ruby 3.2.2参考。

mbjcgjjk

mbjcgjjk1#

你可以清理你的例子,但是不能保证它对任何不是存储在字符串中的平面符号数组的东西都有效。

s = "[:test1, :test2]"

s = s.delete(":[]").split(?,).map { _1.strip.to_sym }
=> [:test1, :test2]

然而,我们完全不清楚你是如何得到一个String而不是一个Array of Symbols的,Rails指南中关于复合键的内容也没有解释你是如何得到这种状态的。我不得不假设您从表单输入或URI参数(两者都表示为字符串)获取数据,而不是将元素解析为 * 单独的 * 列名,以便您可以从两个String值正确地形成复合键。
关于表单中的复合键的部分说,键必须用下划线分隔,这样Rails才能发挥作用,而且您可能还需要在控制器中实现params.extract_value才能正确访问它们。您没有显示任何控制器、视图或模型代码,因此看起来您试图修补格式不正确的最终结果,而不是关注错误数据的来源。
即使事实并非如此,这最终也解决了错误的问题。你需要弄清楚为什么 s 包含无效的JSON,或者为什么你没有得到两个单独的键作为String对象(而不是Symbols),然后你可以使用它们来正确地形成你的复合键。
解决这些问题。事后的字符串munging是解决症状,而不是原因。

46scxncf

46scxncf2#

与其删除你不想要的比特,我建议你提取你想要的字符串(然后将它们转换为符号)。

s = "[:test1, :test2]"
s.scan(/\w+/).map(&:to_sym)
  #=> [:test1, :test2]

请参阅String#scan和String#to_sym。
如果需要,可以稍微修改正则表达式以确认每个冒号都存在。

s.scan(/:\K\w+/).map(&:to_sym)
  #=> [:test1, :test2]

s.scan(/(?<=:)\w+/).map(&:to_sym)
  #=> [:test1, :test2]

在这里,\K将匹配的开始重置到字符串中的当前位置,并丢弃之前使用的任何字符(":")。(?<=:)是一个 * 正向后查找 *,它Assert匹配的开始后面紧跟着一个冒号。
然而,这些改进的价值值得怀疑。例如,如果字符串是"[test1, :test2]",则将返回[:test2],而引发ArgumentError异常可能更合适。如果字符串格式的正确性有问题,那么通过执行以下命令来执行单独的检查可能会更有用。

s.match?(/\A *\[ *:\w+(?: *, *:\w+)* *\] *\z/)

Demo 1
这个正则表达式可以用 free-spacing 模式表示,使其具有自文档性。

/
\A          # match the beginning of the string
\ *\[\ *    # match >= 0 spaces followed by '[', followed by >= 0 spaces 
:\w+        # match ':' followed by >= 1 word characters
(?:         # begin a non-capture group
   \ *,\ *  # match >= 0 spaces followed by ',', followed by >= 0 spaces
   :\w+     # match ':' followed by >= 1 word characters
)*          # end the non-capture group and execute it >= 0 times
\ *\]\ *    # match >= 0 spaces followed by '', followed by >= 0 spaces
\z          # match the end of the string
/x          # invoke free-spacing mode

在自由间距模式下,必须保护表达式中的所有空格。我是通过逃离空格来实现的将每个空格放在一个字符类([ ])中是另一种常见的方法。
1.为了测试多个字符串,我在链接中使用了行首(^)和行尾($)锚点,而不是字符串首(\A)和字符串尾(\z)锚点。

相关问题