# Splitting a string with escapable separator?

**URL:** <https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156>\
**Category:** ruby-talk\
**Created:** [27 September 2005 23:46 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156 "2005-09-27T23:46:44Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Michael\_Schuerig](https://avatars.discourse-cdn.com/v4/letter/m/54ee81/32.png) [@Michael\_Schuerig](https://rubytalk.org/u/Michael_Schuerig)\
**Post date:** [27 September 2005 23:46 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156/1 "2005-09-27T23:46:44Z")

</div>

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

&nbsp;&nbsp;"Hello\, World,Hi".split\_escapable(',' '\')  
&nbsp;&nbsp;# =\> ["Hello, World", "Hi"]

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

> **···**
>
> --  
> Michael Schuerig All good people read good books  
> [mailto:michael@schuerig.de](mailto:michael@schuerig.de) Now your conscience is clear  
> [http://www.schuerig.de/michael/](http://www.schuerig.de/michael/) --Tanita Tikaram, Twist In My Sobriety

---

<div class="post-metadata">

**Author:** ![Gavin\_Kistner2](https://avatars.discourse-cdn.com/v4/letter/g/b9e5f3/32.png) [@Gavin\_Kistner2](https://rubytalk.org/u/Gavin_Kistner2)\
**Post date:** [28 September 2005 05:00 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156/2 "2005-09-28T05:00:37Z")

</div>

Your above example is missing a couple of \, but I assume I know what you meant.

Is the following elegant or not?

class String  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def split\_escapable( separator, escape\_char=nil )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;results =   
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;re = /(.+?)(?:#{escape\_char ? "([^\\#{escape\_char}])" : ''}#{separator}|$)/  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.scan( re ){ |str,last\_char|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;results \<\< str + last\_char.to\_s  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;results  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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
> > 
> > &nbsp;&nbsp;"Hello\, World,Hi".split\_escapable(',' '\')  
> > &nbsp;&nbsp;# =\> ["Hello, World", "Hi"]
> > 
> > Through a number of permutations with regexps, scan and the rest of the  
> > family, I was unable to find a solution.

---

<div class="post-metadata">

**Author:** ![W\_James](https://avatars.discourse-cdn.com/v4/letter/w/e274bd/32.png) [@W\_James](https://rubytalk.org/u/W_James)\
**Post date:** [28 September 2005 10:26 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156/3 "2005-09-28T10:26:44Z")

</div>

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
> 
> &nbsp;&nbsp;"Hello\, World,Hi".split\_escapable(',' '\')  
> &nbsp;&nbsp;# =\> ["Hello, World", "Hi"]
> 
> 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  
&nbsp;&nbsp;def split\_escapable( splitter, escaper )  
&nbsp;&nbsp;&nbsp;&nbsp;escaper = escaper\*2 if escaper=='\\'  
&nbsp;&nbsp;&nbsp;&nbsp;re = %r{ \G  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Make sure at least 1 character remains.  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?= . )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[^#{ splitter }#{ escaper }]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?: #{ escaper } . )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;) \*  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#{ splitter }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\Z  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}xm  
&nbsp;&nbsp;&nbsp;&nbsp;scan( re ).map{|x| x.first.gsub( /#{escaper}(.)/, '\1' ) }  
&nbsp;&nbsp;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}

---

<div class="post-metadata">

**Author:** ![Jason\_Sweat](https://avatars.discourse-cdn.com/v4/letter/j/53a042/32.png) [@Jason\_Sweat](https://rubytalk.org/u/Jason_Sweat)\
**Post date:** [28 September 2005 11:02 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156/4 "2005-09-28T11:02:43Z")

</div>

> 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
> 
> &nbsp;&nbsp;"Hello\, World,Hi".split\_escapable(',' '\')  
> &nbsp;&nbsp;# =\> ["Hello, World", "Hi"]
> 
> 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"]

$ ruby --v  
ruby 1.9.0 (2005-09-08) [i686-linux]

Regards,  
Jason  
[http://blog.casey-sweat.us/](http://blog.casey-sweat.us/)

> **···**
>
> On 9/27/05, Michael Schuerig \<michael@schuerig.de\> wrote:

---

<div class="post-metadata">

**Author:** ![Han\_Holl](https://avatars.discourse-cdn.com/v4/letter/h/d2c977/32.png) [@Han\_Holl](https://rubytalk.org/u/Han_Holl)\
**Post date:** [29 September 2005 10:18 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156/5 "2005-09-29T10:18:35Z")

</div>

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
> > 
> > "Hello\, World,Hi".split\_escapable(',' '\')  
> > # =\> ["Hello, World", "Hi"]

---

<div class="post-metadata">

**Author:** ![David\_Tran1](https://avatars.discourse-cdn.com/v4/letter/d/51bf81/32.png) [@David\_Tran1](https://rubytalk.org/u/David_Tran1)\
**Post date:** [4 October 2005 14:56 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156/6 "2005-10-04T14:56:49Z")

</div>

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
> > 
> > &nbsp;&nbsp;"Hello\, World,Hi".split\_escapable(',' '\')  
> > &nbsp;&nbsp;# =\> ["Hello, World", "Hi"]
> > 
> > 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
> > 
> > --  
> > Michael Schuerig All good people read good books  
> > [mailto:michael@schuerig](mailto:michael@schuerig).de Now your conscience is clear  
> > [Michael Schürig | Sentenced to making sense](http://www.schuerig.de/michael/) --Tanita Tikaram, Twist In My Sobriety

---

<div class="post-metadata">

**Author:** ![Michael\_Schuerig](https://avatars.discourse-cdn.com/v4/letter/m/54ee81/32.png) [@Michael\_Schuerig](https://rubytalk.org/u/Michael_Schuerig)\
**Post date:** [28 September 2005 14:56 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156/7 "2005-09-28T14:56:44Z")

</div>

Jason Sweat wrote:

> **···**
>
> > 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
> > > 
> > > &nbsp;&nbsp;"Hello\, World,Hi".split\_escapable(',' '\')  
> > > &nbsp;&nbsp;# =\> ["Hello, World", "Hi"]
> > > 
> > > 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.
> 
> Michael
> 
> --  
> Michael Schuerig Airtight arguments have  
> [mailto:michael@schuerig](mailto:michael@schuerig).de vacuous conclusions.  
> [Michael Schürig | Sentenced to making sense](http://www.schuerig.de/michael/) --A.O. Rorty, Explaining Emotions

---

<div class="post-metadata">

**Author:** ![Michael\_Schuerig](https://avatars.discourse-cdn.com/v4/letter/m/54ee81/32.png) [@Michael\_Schuerig](https://rubytalk.org/u/Michael_Schuerig)\
**Post date:** [28 September 2005 15:16 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156/8 "2005-09-28T15:16:44Z")

</div>

William James wrote:

> 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.

[snip]

> class String  
> &nbsp;&nbsp;def split\_escapable( splitter, escaper )

[snip]

> &nbsp;&nbsp;end  
> end

Thanks, that appears to work indeed, although I can't claim to  
understand how or why.

Michae.

> **···**
>
> --  
> Michael Schuerig The more it stays the same,  
> [mailto:michael@schuerig](mailto:michael@schuerig).de The less it changes!  
> [Michael Schürig | Sentenced to making sense](http://www.schuerig.de/michael/) --Spinal Tap, The Majesty of Rock

---

<div class="post-metadata">

**Author:** ![Michael\_Schuerig](https://avatars.discourse-cdn.com/v4/letter/m/54ee81/32.png) [@Michael\_Schuerig](https://rubytalk.org/u/Michael_Schuerig)\
**Post date:** [28 September 2005 15:16 UTC](https://rubytalk.org/t/splitting-a-string-with-escapable-separator/21156/9 "2005-09-28T15:16:45Z")

</div>

Gavin Kistner wrote:  
[snip]

> 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}|$)/

Thanks.

Michael

> **···**
>
> --  
> Michael Schuerig The more it stays the same,  
> [mailto:michael@schuerig](mailto:michael@schuerig).de The less it changes!  
> [Michael Schürig | Sentenced to making sense](http://www.schuerig.de/michael/) --Spinal Tap, The Majesty of Rock
