简单的Sinatra应用程序中不兼容的字符编码

时间:2012-04-26 21:16:42

标签: ruby character-encoding sinatra

我有一个非常简单的运行在Ruby 1.9.3上的Sinatra应用程序,它使用ERB和markdown模板。我已经剥离它以证明问题。

这是在Mac OS X Snow Leopard上运行Sinatra 1.3.2。对于降价我正在使用rdiscount 1.6.8。

主Ruby文件包含

get '/services' do
  erb :services
end

services.erb文件中包含以下内容

<%= markdown :'content/service1' %>
£

在markdown文件中我只有一行

£

当我运行Sinatra应用程序并加载'services'页面时,我在ERB文件的第二行(仅包含'£'的那一行)上获得异常Encoding::CompatibilityError at /services incompatible character encodings: UTF-8 and ASCII-8BIT

我做了很多谷歌搜索,我不能为我的生活弄清楚为什么会发生这种情况。 ERB和markdown文件在我的本地磁盘上是UTF-8,但显然它们被Sinatra加载并变成字符串,我不知道如何判断这些字符串的编码。

如果我强制Sinatra使用ASCII-8BIT(通过将settings.default_encoding = 'ASCII-8BIT'添加到我的主Sinatra Ruby文件的顶部),则不会抛出任何异常但是'£'字符看起来不对。

任何指针?

1 个答案:

答案 0 :(得分:15)

这是Tilt中的问题,这是Sinatra使用的模板系统(并且正在考虑用于Rails)。查看问题#75#107

问题主要取决于Tilt reads template files from the disk - 它如何使用binread。这意味着传递给实际模板引擎的源字符串具有ASCII-8BIT的关联编码,这基本上是说它是未知的。

RDiscount有code to set the encoding of the output to match the input,但当输入编码为ASCII-8BIT时,这没有太大帮助;结果给出相同的编码。 Kramdown也会发生同样的事情(或者类似的事情),所以简单的切换不会解决这个问题。

当模板具有非ascii字符(即£)并且您尝试将结果与其他utf-8编码的字符串组合时,这会导致问题。如果模板只包含ascii字符,则它与utf-8兼容,Ruby可以组合两个字符串。如果没有,则会获得您看到的CompatibilityError

一种可行的解决方法是自己读取模板文件,并将带有正确编码的结果字符串传递给Tilt:

<%= markdown File.read './views/pound.md' %>
£

通过read而不是binread自行阅读文件,您可以确保它具有正确的编码,因此与erb文件的其余部分兼容。您可能想要一次读取该文件,并在尝试此操作时将内容缓存到某处。

另一种解决方法是捕获markdown方法的输出并在其上使用force_encoding

<%= markdown(:pound).force_encoding('utf-8') %>
£

这是可能的,因为虽然编码是ASCII-8BIT,但是你知道字符串中的字节确实是utf-8编码的,所以你只需要改变编码。