views:

951

answers:

2

As part of a larger series of operations, I'm trying to take tokenized chunks of a larger string and get rid of punctuation, non-word gobbledygook, etc. My initial attempt used String#gsub and the \W regexp character class, like so:

my_str = "Hello,"
processed = my_str.gsub(/\W/,'')
puts processed # => Hello

Super, super, super simple. Of course, now I'm extending my program to deal with non-Latin characters, and all heck's broken loose. Ruby's \W seems to be something like [^A-Za-z0-9_], which, of course, excludes stuff with diacritics (ü, í, etc.). So, now my formerly-simple code crashes and burns in unpleasent ways:

my_str = "Quística."
processed = my_str.gsub(/\W/,'')
puts processed # => Qustica

Notice that gsub() obligingly removed the accented "í" character. One way I've thought of to fix this would be to extend Ruby's \W whitelist to include higher Unicode code points, but there are an awful lot of them, and I know I'd miss some and cause problems down the line (and let's not even start thinking about non-Latin languages...). Another solution would be to blacklist all the stuff I want to get rid of (punctuation, $/%/&/™, etc.), but, again, there's an awful lot of that and I really don't want to start playing blacklist-whack-a-mole.

Has anybody out there found a principled solution to this problem? Is there some hidden, Unicode-friendly version of \W that I haven't discovered yet? Thanks!

+4  A: 

You need to run ruby with the "-Ku" option to make it use UTF-8. See the documentation for command-line options. This is what happens when I do this with irb:

% irb -Ku
irb(main):001:0> my_str = "Quística."
=> "Quística."
irb(main):002:0> processed = my_str.gsub(/\W/,'')
=> "Quística"
irb(main):003:0>

You can also put it on the #! line in your ruby script:

#!/usr/bin/ruby -Ku
wdebeaum
Gah. I thought I already was in UTF-8 mode. That sorts things out, thanks for the help!
Steven Bedrick
+1  A: 

I would just like to add that in 1.9.1 it works by default.

$ irb
ruby-1.9.1-p243 > my_str = "Quística."
=> "Quística."
ruby-1.9.1-p243 > processed = my_str.gsub(/\W/,'')
=> "Quística"
ruby-1.9.1-p243 > processed.encoding
=> #<Encoding:UTF-8>

PS. Nothing beats rvm for trying out different versions of Ruby. DS.

Jonas Elfström
Ooooh, that's certainly nice to see. I haven't gotten around to playing with 1.9 yet, but I'm glad to see that it addresses some of 1.8's character encoding quirks.
Steven Bedrick
It doesn't just address some of them, it addresses all of them. And all of Java's, C++'s, Python's, PHP's, ..., too. Ruby 1.9's encoding system is probably the most powerful, most complete evar, with the possible exception of only ELisp. It also *looks* insanely complicated, but that is because encoding *is* complicated. Java's encoding may *look* simpler, but have you ever seen a moderately complex piece of Java that actually *uses* `String`? No, all parsers, decoders, compilers, Regexp engines, XML libraries actually use `byte[]`, exactly *because* `String` is too simplistic.
Jörg W Mittag
Well, I'll definitely have to check it out soon, then. I swear, if I could trade, say, a kidney for never having to deal with another character encoding issue again for the rest of my life, I might actually consider the deal. I mean, forget all the truly big, complicated encoding issues- just considering the stupid little ones like the one I described in the original question, how many collective hours of our lives have we wasted dealing with this crap? I'll tell you: Way. Too. Many.
Steven Bedrick