I'm trying to come up with an *elegant* way to split a string into an
array at a separator with the additional feature that the separators
can be escaped. It should work like this
Through a number of permutations with regexps, scan and the rest of the
family, I was unable to find a solution. I could parse the given string
myself, going though it character by character, but I'd prefer a less
pedestrian approach.
Your above example is missing a couple of \, but I assume I know what you meant.
Is the following elegant or not?
class String
def split_escapable( separator, escape_char=nil )
results =
re = /(.+?)(?:#{escape_char ? "([^\\#{escape_char}])" : ''}#{separator}|$)/
self.scan( re ){ |str,last_char|
results << str + last_char.to_s
}
results
end
end
p "Hello\\, World,Hi".split_escapable( ',', '\\' )
#=> ["Hello\\, World", "Hi"]
Note that the above does not account for the case of:
Hello \\,World
(where an escaped backslash is intended to end the first entry)
but if that was important, that's just a matter of a bit of odd/even backslash counting.
Something like (untested):
re = /(.+?)(?:#{escape_char ? "([^\\#{escape_char}](\\#{escape_char}\\#{escape_char})*)" : ''}#{separator}|$)/
···
On Sep 27, 2005, at 5:46 PM, Michael Schuerig wrote:
I'm trying to come up with an *elegant* way to split a string into an
array at a separator with the additional feature that the separators
can be escaped. It should work like this
I'm trying to come up with an *elegant* way to split a string into an
array at a separator with the additional feature that the separators
can be escaped. It should work like this
Through a number of permutations with regexps, scan and the rest of the
family, I was unable to find a solution. I could parse the given string
myself, going though it character by character, but I'd prefer a less
pedestrian approach.
class String
def split_escapable( splitter, escaper )
escaper = escaper*2 if escaper=='\\'
re = %r{ \G
# Make sure at least 1 character remains.
(?= . )
(
(?:
[^#{ splitter }#{ escaper }]
>
(?: #{ escaper } . )
) *
)
(?:
#{ splitter }
>
\Z
)
}xm
scan( re ).map{|x| x.first.gsub( /#{escaper}(.)/, '\1' ) }
end
end
s = <<HERE
Hello@, World!,Hi.
Alarm rings@, lights flash.,One escaper @@
HERE
s.split("\n").each {|x|a=x.split_escapable(',','@');p a; puts a}
puts "----"
s = <<'HERE'
Hello\, World!,Hi.
Alarm rings\, lights flash.,One escaper \\
HERE
s.split("\n").each {|x|a=x.split_escapable(',','\\');p a; puts a}
I'm trying to come up with an *elegant* way to split a string into an
array at a separator with the additional feature that the separators
can be escaped. It should work like this
Through a number of permutations with regexps, scan and the rest of the
family, I was unable to find a solution. I could parse the given string
myself, going though it character by character, but I'd prefer a less
pedestrian approach.
Michael
With the new Regex engine in cvs ruby you can use a negative lookback
assertion in your split:
None of the offerings so far had support for the second argument for split.
The following is probably quite efficent if the chance of an occurrence of
an escaped character is low.
(I haven't benchmarked anything, though). Also, it's not suitable for binary
strings.
class String
def split_escapable(separator, escape_char, *args)
istr = dup
impossible = "\x01"
replace = "#{escape_char}#{separator}"
changed = istr.gsub!(replace, impossible)
fields = istr.split(separator, *args)
if changed
fields.each do |f|
f.gsub!(impossible, separator)
end
end
fields
end
end
a = "Hello\\, World,Hi"
puts a.split_escapable( ',', '\\' )
Cheers,
Han Holl
···
On 9/28/05, Michael Schuerig <michael@schuerig.de> wrote:
I'm trying to come up with an *elegant* way to split a string into an
array at a separator with the additional feature that the separators
can be escaped. It should work like this
Without 1.9 lookback, you could try:
'Hello\,World,Hi'.scan(/(?:\\,|[^,])+/).map {|e| e.tr('\\','')}
=> ["Hello,World", "Hi"]
Michael Schuerig wrote:
···
I'm trying to come up with an *elegant* way to split a string into an
array at a separator with the additional feature that the separators
can be escaped. It should work like this
Through a number of permutations with regexps, scan and the rest of the
family, I was unable to find a solution. I could parse the given string
myself, going though it character by character, but I'd prefer a less
pedestrian approach.
On 9/27/05, Michael Schuerig <michael@schuerig.de> wrote:
I'm trying to come up with an *elegant* way to split a string into an
array at a separator with the additional feature that the separators
can be escaped. It should work like this
Through a number of permutations with regexps, scan and the rest of
the family, I was unable to find a solution. I could parse the given
string myself, going though it character by character, but I'd prefer
a less pedestrian approach.
Michael
With the new Regex engine in cvs ruby you can use a negative lookback
assertion in your split:
s = "Hello\\, World, Hi"
=> "Hello\\, World, Hi"
s.split /(?<!\\),/
=> ["Hello\\, World", " Hi"]
That must be the most elegant solution. Unfortunately I can't use cvs
ruby and can't wait for it either.
I'm trying to come up with an *elegant* way to split a string into an
array at a separator with the additional feature that the separators
can be escaped.
[snip]
class String
def split_escapable( splitter, escaper )
[snip]
end
end
Thanks, that appears to work indeed, although I can't claim to
understand how or why.
Note that the above does not account for the case of:
Hello \\,World
(where an escaped backslash is intended to end the first entry)
but if that was important, that's just a matter of a bit of odd/even
backslash counting.
That's a thing I'd need. Opportunistically, I'll go with William's
suggestion from a sibling post.
Something like (untested):
re = /(.+?)(?:#{escape_char ? "([^\\#{escape_char}](\\#{escape_char}\
\#{escape_char})*)" : ''}#{separator}|$)/