iconv will be deprecated in the future, transliterate - ruby

ruby 1.9.3 is warning about iconv deprecation, but I use iconv to remove diacritic to have plain ASCII from
Iconv.iconv('asccii//translit', 'utf-8', 'Těžiště')
returns Teziste. How I can obtain this using String.encode?

If I had Rails (or just ActiveSupport) around, I'd do something like this:
ActiveSupport::Multibyte::Unicode.normalize('Těžiště', :kd).chars.grep(/\p{^Mn}/).join('')
to get 'Teziste'. The :kd essentially decomposes your accented characters into separate accents and characters and then the \p{^Mn} removes all the non-spacing marks from the character stream and when you put it all back together with join, you get the unaccented string back.
If you don't have Rails or ActiveSupport handy, then you could use UnicodeUtils.compatibility_decomposition from unicode-utils instead of ActiveSupport::Multibyte::Unicode.normalize:
> UnicodeUtils.compatibility_decomposition('Těžiště').chars.grep(/\p{^Mn}/).join('')
=> "Teziste"
I tend to have the ActiveSupport version patched into String in Rails-land:
def de_accent
#
# `\p{Mn}` is also known as `\p{Nonspacing_Mark}` but only the short
# and cryptic form is documented.
#
ActiveSupport::Multibyte::Unicode.normalize(self, :kd).chars.grep(/\p{^Mn}/).join('')
end
so that I can say things like:
> s = 'Těžiště'.de_accent
=> "Teziste"
to strip out accents.
This approach won't handle everything but maybe it will do enough.

Related

Converting UTF-8 characters into properly ASCII characters

I have the string "V\355ctor" (I think that's Víctor).
Is there a way to convert it to ASCII where í would be replaced by an ASCII i?
I already have tried Iconv without success.
(I'm only getting Iconv::IllegalSequence: "\355ctor")
Further, are there differences between Ruby 1.8.7 and Ruby 2.0?
EDIT:
Iconv.iconv('UTF-8//IGNORE', 'UTF-8', "V\355ctor") this seems to work but the result is Vctor not Victor
I know of two options.
transliterate from the I18n gem.
$ irb
1.9.3-p448 :001 > string = "Víctor"
=> "Víctor"
1.9.3-p448 :002 > require 'i18n'
=> true
1.9.3-p448 :003 > I18n.transliterate(string)
=> "Victor"
Unidecoder from the stringex gem.
Stringex::Unidecoder..decode(string)
Update:
When running Unidecoder on "V\355ctor", you get the following error:
Encoding::CompatibilityError: incompatible encoding regexp match (UTF-8 regexp with IBM437 string)
Hmm, maybe you want to first translate from IBM437:
string.force_encoding('IBM437').encode('UTF-8')
This may help you get further. Note that the autodetected encoding could be incorrect, if you know exactly what the encoding is, it would make everything a lot easier.
What you want to do is called transliteration.
The most used and best maintained library for this is ICU. (Iconv is frequently used too, but it has many limitations such as the one you ran into.)
A cursory Google search yields a few ruby ICU wrappers. I'm afraid I cannot comment on which one is better, since I've admittedly never used any of them. But that is the kind of stuff you want to be using.

Ruby 2.0 iconv replacement

I don't know Ruby but want to run an script where:
D:/Heather/Ruby/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require': cannot load such file -- iconv (LoadError)
it works somehow if I comment iconv code but it will be much better if I can recode this part:
return Iconv.iconv('UTF-8//IGNORE', 'UTF-8', (s + ' ') ).first[0..-2]
without iconv. Maybe I can use String#encode here somehow?
Iconv was deprecated (removed) in 1.9.3.
You can still install it.
Reference Material if you unsure:
https://rvm.io/packages/iconv/
However the suggestion is that you don't and rather use:
string.encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "?")
API
String#scrub can be used since Ruby 2.1.
str.scrub(''),
str.scrub{ |bytes| '' }
Related question: Equivalent of Iconv.conv(“UTF-8//IGNORE”,…) in Ruby 1.9.X?
If you're not on Ruby 2.1, so can't use String#scrub then the following will ignore all parts of the string that aren't correctly UTF-8 encoded.
string.encode('UTF-16', :invalid => :replace, :replace => '').encode('UTF-8')
The encode method does almost exactly what you want, but with the caveat that encode doesn't do anything if it thinks the string is already UTF-8. So you need to change encodings, going via an encoding that can still encode the full set of unicode characters that UTF-8 can encode. (If you don't you'll corrupt any characters that aren't in that encoding - 7bit ASCII would be a really bad choice!)
I have not had luck with the various approaches using a one line string.encode by itself
But I wrote a backfill that implements String#scrub in MRI pre 2.1, or other rubies that do not have it.
https://github.com/jrochkind/scrub_rb

ruby incorrect method behavior (possible depending charset)

I got weird behavior from ruby (in irb):
irb(main):002:0> pp "    LS 600"
"\302\240\302\240\302\240\302\240LS 600"
irb(main):003:0> pp "    LS 600".strip
"\302\240\302\240\302\240\302\240LS 600"
That means (for those, who don't understand) that strip method does not affect this string at all, same with gsub('/\s+/', '')
How can I strip that string (I got it while parsing Internet page)?
The string "\302\240" is a UTF-8 encoded string (C2 A0) for Unicode code point A0, which represents a non breaking space character. There are many other Unicode space characters. Unfortunately the String#strip method removes none of these.
If you use Ruby 1.9.2, then you can solve this in the following way:
# Ruby 1.9.2 only.
# Remove any whitespace-like characters from beginning/end.
"\302\240\302\240LS 600".gsub(/^\p{Space}+|\p{Space}+$/, "")
In Ruby 1.8.7 support for Unicode is not as good. You might be successful if you can depend on Rails's ActiveSupport::Multibyte. This has the advantage of getting a working strip method for free. Install ActiveSupport with gem install activesupport and then try this:
# Ruby 1.8.7/1.9.2.
$KCODE = "u"
require "rubygems"
require "active_support/core_ext/string/multibyte"
# Remove any whitespace-like characters from beginning/end.
"\302\240\302\240LS 600".mb_chars.strip.to_s

Iconv and Kconv on Ruby (1.9.2)

I know that Iconv is used to convert strings' encoding.
From my understandings Kconv is for the same purpose (am I wrong?).
My question is: what is the difference between them, and what should I use for encoding conversions.
btw found some info that Iconv will be deprecated from 1.9.3 version.
As https://stackoverflow.com/users/23649/jtbandes says, it looks Kconv is like Iconv but specialized for Kanji ("the logographic Chinese characters that are used in the modern Japanese writing system along with hiragana" http://en.wikipedia.org/wiki/Kanji). Unless you are working on something specifically Japanese, I'm guessing you don't need Kconv.
If you're using Ruby 1.9, you can use the built-in encoding support most of the time instead of Iconv. I tried for hours to understand what I was doing until I read this:
http://www.joelonsoftware.com/articles/Unicode.html
Then you can start to use stuff like
String#encode # Ruby 1.9
String#encode! # Ruby 1.9
String#force_encoding # Ruby 1.9
with confidence. If you have more complex needs, do read http://blog.grayproductions.net/categories/character_encodings
UPDATED Thanks to JohnZ in the comments
Iconv is still useful in Ruby 1.9 because it can transliterate characters (something that String#encode et al. can't do). Here's an example of how to extend String with a function that transliterates to UTF-8:
require 'iconv'
class ::String
# Return a new String that has been transliterated into UTF-8
# Should work in Ruby 1.8 and Ruby 1.9 thanks to http://po-ru.com/diary/fixing-invalid-utf-8-in-ruby-revisited/
def as_utf8(from_encoding = 'UTF-8')
::Iconv.conv('UTF-8//TRANSLIT', from_encoding, self + ' ')[0..-2]
end
end
"foo".as_utf8 #=> "foo"
"foo".as_utf8('ISO-8859-1') #=> "foo"
Thanks JohnZ!

Ruby's String#gsub, unicode, and non-word characters

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

Resources