我有这样的自定义语法:
###############
Heading 1
###############
Body1
Body1
###############
Heading 2
###############
Body2
Body2
我想我可以用scan
拆分每个部分,但由于“Ruby - Splitting multiple strings with scan”中解释的原因,这并不是那么简单。
理想情况下,我只想指定每个部分的DRY正则表达式,例如:
/^\#+\s+(^.*)\#+\s+(^.*)\s+/
投掷scan
或类似内容会为每个部分返回[headerText, bodyText]
数组。
(我意识到这与MarkDown一样,但我想添加一个自定义结构和类属性。)
答案 0 :(得分:4)
您知道您的示例有效吗?Markdown?
您可以让Markdown处理器为您完成工作。一个例子是Kramdown。除了转换为各种输出格式之外,它还可以创建自己的类似DOM的内部表示,您可以遍历:
require 'kramdown'
d = Kramdown::Document.new(text)
puts d.root.children.map(&:type)
#=> [:header, :p, :blank, :p, :blank, :header, :p, :blank, :p]
您可以使用各种方法来获取内容。
事实上,如果你坚持使用Markdown作为你的格式,你可以获得很多表现力,而且只需要很少的努力。
*虽然有效,但建议对标题格式进行微小更改,以便标记以识别标题文本
答案 1 :(得分:2)
答案 2 :(得分:0)
尝试以下
source = <<EOF
###############
Heading 1
###############
Body1
Body1
###############
Heading 2
###############
Body2
Body2
EOF
groups = source.scan /(#*\n([^#]*)#*\n([^#]*))/
groups[0][1,2]
groups[1][1,2]
我得到输出
["Heading 1\n", "\nBody1\nBody1\n\n"]
["Heading 2\n", "\nBody2\nBody2\n"]
扫描包括组的组,因此组是数组的数组,最外面的数组是每个Header,Body组,第一个索引包括两个,索引1和2是Header和Body。
要访问所有组,请使用
groups.each do | group |
puts group[1,2]
end
应该导致类似
的内容Heading 1
Body1
Body1
Heading 2
Body2
Body2
答案 3 :(得分:0)
与String#scan
一起使用时,这个正则表达式可以为您提供所需内容:
/(#+)(?<heading>[^#]*)(#+)(?<body>[^#\z]*)/
以下是我使用示例字符串收到的输出。
regex = /(#+)(?<heading>[^#]*)(#+)(?<body>[^#\z]*)/
string = "###############\nHeading 1\n###############\n\nBody1\nBody1\n\n###############\nHeading 2\n###############\n\nBody2\nBody2"
string.scan regex
=> [["\nHeading 1\n", "\n\nBody1\nBody1\n\n"], ["\nHeading 2\n", "\n\nBody2\nBody2"]]
拉出的字符串有一些需要删除的换行符。我试图改进正则表达式来消除它们,但是很难让最后一部分的主体正确解析。虽然清理起来并不算太糟糕。
string.scan(regex).map { |section| section.map(&:strip) }
=> [["Heading 1", "Body1\nBody1"], ["Heading 2", "Body2\nBody2"]]
答案 4 :(得分:0)
这是使用slice_before
的好机会:
text = <<EOT
###############
Heading 1
###############
Body1
Body1
###############
Heading 2
###############
Body2
Body2
EOT
chunks = text.split("\n")
.reject{ |s| s.strip.empty? || s[/^#+$/] }
.slice_before(/^Heading/)
.to_a
chunks # => [["Heading 1", "Body1", "Body1"], ["Heading 2", "Body2", "Body2"]]
这里发生了什么:
text.split("\n").reject{ |s| s.strip.empty? || s[/^#+$/] }
# => ["Heading 1", "Body1", "Body1", "Heading 2", "Body2", "Body2"]
slice_before
遍历数组,寻找与其模式匹配的内容。一旦找到它,它就会生成一个新的子数组并继续查找。最终结果是一个数组数组,每个子数组包含每个目标之间的元素/行。
答案 5 :(得分:0)
虽然不像单个正则表达式那样紧凑,但以下方法可能更容易调试,测试和理解:
str.gsub(/^#+\n\n+/,'')
.gsub(/^#+\n/,'')
.split(/\n\n+/)
.map { |s| s.split("\n") }
#=> [["Heading 1", "Body1", "Body1"], ["Heading 2", "Body2", "Body2"]]
我通过以下四个步骤中的每个步骤构建此表达式,对其进行测试,然后继续进行下一步。当所有步骤都正常工作时,我只是将它们链接在一起。我假设块被一个或多个空行分隔。
str =<<THE_END
###############
Heading 1
###############
Body1
Body1
###############
Heading 2
###############
Body2
Body2
THE_END
# remove lines ###\n\n+ between each heading and body
s1 = str.gsub(/^#+\n\n+/,'')
#=> "###############\nHeading 1\nBody1\nBody1\n\n" +
# "###############\nHeading 2\nBody2\nBody2\n"
# remove line ###\n above each header
s2 = s1.gsub(/^#+\n/,'')
#=> "Heading 1\nBody1\nBody1\n\n" +
# "Heading 2\nBody2\nBody2\n"
# split on remaining blank lines
s3 = s2.split(/\n\n+/)
#=> ["Heading 1\nBody1\nBody1", "Heading 2\nBody2\nBody2\n"]
# split each string in array into heading and body elements
s3.map { |s| s.split(/\n/) }
#=> [["Heading 1", "Body1", "Body1"], ["Heading 2", "Body2", "Body2"]]