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

Ben*_*Ben 7 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
Run Code Online (Sandbox Code Playgroud)

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

<%= markdown :'content/service1' %>
£
Run Code Online (Sandbox Code Playgroud)

在markdown文件中我只有一行

£
Run Code Online (Sandbox Code Playgroud)

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

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

如果我强迫Sinatra使用ASCII-8BIT(通过添加settings.default_encoding = 'ASCII-8BIT'到我的主要Sinatra Ruby文件的顶部),则不会抛出任何异常但是'£'字符出现错误.

有什么指针吗?

mat*_*att 15

这是Sintra使用的模板系统Tilt中的一个问题(正在考虑用于Rails).看看问题#75#107.

问题基本上取决于Tilt如何从磁盘读取模板文件 - 它使用binread.这意味着传递给实际模板引擎的源字符串具有相关的编码ASCII-8BIT,这基本上是说它是未知的.

RDiscount有代码来设置输出的编码以匹配输入,但是当输入编码时,这没有多大帮助ASCII-8BIT; 结果给出相同的编码.Kramdown也会发生同样的事情(或者类似的事情),所以简单的切换不会解决这个问题.

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

一种可能的解决方法是自己读取模板文件,并使用正确的编码将生成的字符串传递给Tilt:

<%= markdown File.read './views/pound.md' %>
£
Run Code Online (Sandbox Code Playgroud)

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

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

<%= markdown(:pound).force_encoding('utf-8') %>
£
Run Code Online (Sandbox Code Playgroud)

这是可能的,因为虽然编码是ASCII-8BIT,但您知道字符串中的字节确实是utf-8编码的,因此您只需更改编码即可.