dimitarvp

dimitarvp

Sharing with the community: text transcoding libraries

Lately I found myself having to process HTML that was not UTF-8 encoded (it was coded in the cyrillic coding page known as cp1251 or Windows-1251). It struck me as very odd that I couldn’t easily change the text to UTF-8 – since Elixir’s String only works with that – so I figured I’ll dig for an hour or two. Here are the results of the 3 libraries I tried.

Codepagex (Elixir)

I very much liked the idea of the library but after trying every possible way to configure it to include cp1251, it still didn’t (UPDATE: see at the end of the section). But basically what happens is (copied from the GitHub page):

iex> Codepagex.from_string("æøåÆØÅ", :iso_8859_1)
{:ok, <<230, 248, 229, 198, 216, 197>>}

iex> Codepagex.to_string(<<230, 248, 229, 198, 216, 197>>, :iso_8859_1)
{:ok, "æøåÆØÅ"}

If you want to inspect what codings are available:

Codepagex.encoding_list() # Can also pass :all to get those who are not supported as well.

Supposedly you can add other codings which aren’t enabled by default through the config – check the linked GitHub page for that. I wasn’t able to make cp1251 even appear in the list of the currently loaded codings even though I used all formats the author says are supported. So I gave it up.

Still, this is the one I like the most. If the codings that are supported out of the box are good enough for you, I recommend the library.

UPDATE by @michalmuskala: you actually can enable those coding pages. Minimal step-by-step:

  1. Add this to your config/config.exs:
config :codepagex, :encodings, [
  "VENDORS/MICSFT/WINDOWS/CP1251"
]
  1. Run this:
mix deps.compile codepagex --force
  1. Do this in iex:
iex> Codepagex.to_string(<<196, 224, 242, 224>>, :"VENDORS/MICSFT/WINDOWS/CP1251")
{:ok, "Дата"}

(/CC-ing the author: @tallakt and apologies for misunderstanding.)

elixir-mbcs (Elixir)

An Elixir wrapper around erlang-mbcs.

To install it I had to include this in my mix.exs:

{:elixir_mbcs, github: "woxtu/elixir-mbcs", tag: "0.1.3"}

It goes like this (copied from the GitHub page):

# Start mbcs server
iex> Mbcs.start
:ok

# Convert UTF-8 to Shift_JIS
iex> Mbcs.encode!("九条カレン", :cp932)
<<139, 227, 143, 240, 131, 74, 131, 140, 131, 147>>

# Convert Shift_JIS to UTF-8, and return as a list
iex> Mbcs.decode!([139, 227, 143, 240, 131, 74, 131, 140, 131, 147], :cp932, return: :list)
[20061, 26465, 12459, 12524, 12531]

It seems to support more codings than Codepagex.

Why I skipped it:

  • I was doing a hobby project inside Windows and as hard as I tried, I couldn’t make MINGW make actually work well enough to compile the .erl files. This is entirely Windows-specific and is in no way damning the library!

  • I dislike having to explicitly start a server so I can use a library. Then again, you can make a very thin wrapper around the starting function, insert it in your supervision tree and forget about that nuisance until the end of time. This didn’t suit me for a quick hobby project but again, is in no way a real drawback of the library.

All in all, if I did my hobby projects in Linux or macOS (working on it, the MBP 2015 still patiently awaits for me to get used to a laptop keyboard which I still can’t :roll_eyes:) and if I had to cover more codings then I would definitely go for this one even though I liked Codepagex API better.

erlyconv (Erlang)

This is the one I ended up using. Install it like so in your mix.exs:

{:erlyconv, github: "eugenehr/erlyconv"}

(I learned that day that you can just import Erlang projects in your Elixir projects and was very pleasantly surprised!)

My example usage:

iex> :erlyconv.from_unicode(:cp1251, "Дата")
<<196, 224, 242, 224>>

iex> :erlyconv.to_unicode(:cp1251, <<196, 224, 242, 224>>) 
"Дата"

No server starts, no wrappers, no need for configuration. Do note it looks like it supports a bit less codings than elixir-mbcs / erlang-mbcs.


Takeaways.

  • I would definitely try and contribute to Codepagex if or when I get the time because its approach looks really good – it works directly with files downloaded from the Unicode organization. If parsing those reliably can lead to maximum support for codings then I’d be all for that.

  • For now I cannot contribute to Erlang libraries due to me not knowing the language well enough but I wouldn’t try to work with erlang-mbcs yet. It’s a bit strange to me that a text transcoding library needs a server or why it needs to invoke make – I am guessing it’s old and it would make use of Erlang’s tooling better if it were written today. But of course I might be ignorant here so can’t really claim anything as a fact. Subjective opinion: erlang-mbcs is the clunkiest of the three. Still, it looks like it supports the most codings.

  • I’d try to help erlyconv because I liked its very minimalistic approach. And I am using it in my hobby project right now and I am very happy to have something that JustWorks™.

I’d love additional input from Erlang folks (@rvirding and @joeerl if they don’t mind being mentioned) or anybody else who has struggled with text transcoding.

Thanks for reading! Hope this was helpful to you.

Most Liked

josevalim

josevalim

Creator of Elixir

How many codepages there are? If a dozen, then you can generate empty modules for all of them that when invoked tell user exactly how they can enable those modules. So you would something like:

You are using CP1234 but we have not compiled it. Please add this to your config and then run mix deps....

Another option is for you to dynamically generate something whenever it is missing but print a warning. That’s what we do for mime: https://github.com/elixir-plug/mime/blob/master/lib/mime/application.ex

tallakt

tallakt

Hi. Id be happy to improve the interface for Codepagex. Some of the encodings are quite large an this is why Codepagex is configurable in the first place. Option 2 seems fine, except it seems a bit clumsy. An option where codepages are dynamically compiled on use seems to be the best option, I’d have to think about how this could be implemented.

Whatever I/we decide here, compatibility will also be an issue here, as Codepagex does compile a few codepages by default, and changing the selection algorithm could break code

Speaking on the subject of Codepagex, there is still one important feature missing which is support for io lists. These would boost performance as for many conversins, string parts could be reused..

michalmuskala

michalmuskala

Have you explicitly recompiled the codepagex package with mix deps.compile codepagex --force after changing the configuration? Dependencies don’t pick up configuration changes automatically if they read it at compile-time.

Last Post!

darraghenright

darraghenright

I’m very glad I decided to do a search here… remind me to do it more often :smiley: This is incredibly useful information.

I was just looking at Codepagex, and had given up because it wasn’t apparent to me how I could use it to convert cp1252 encoded strings. I guess I could have read the docs a bit better.

It’s a bit of a pity that File.open, File.stream etc. don’t accept encodings like cp1252 but I guess this is where a good library comes in useful.

Excellent writeup @dimitarvp — thanks!

Where Next?

Popular in Discussions Top

PragTob
Hey everyone, this has been on my mind for some time and I’d love your input on it! TLDR: I feel like maps are superioer for storing and...
New
CharlesO
Erlang :list.nth simple, but 1 - based nth(1, [H|_]) -&gt; H; nth(N, [_|T]) when N &gt; 1 -&gt; nth(N - 1, T). Elixir Enum.at … coo...
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31695 143
New
tmbb
This is a post to discuss the new Phoenix LiveView functionality. From Chris’s talk, it appears that they generate all HTML on the serve...
342 18584 126
New
chuck
Let me start by stating an assumption: Phoenix is a great approach to building REST APIs. There are many reasons for this, but I will ass...
New
crispinb
On reading dhh’s latest The One Person Framework it strikes me that Phoenix with LiveView is already pretty much this. However, never hav...
New
shishini
I think this twitter post and youtube video didn’t get as much attention as I hoped I am still new to Elixir, so can’t really judge ...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 130286 1222
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31494 112
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New

We're in Beta

About us Mission Statement