7

我有一个在 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' %>
£

在降价文件中,我只有一行

£

当我运行 Sinatra 应用程序并加载“服务”页面时,我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 文件的顶部),则不会引发异常,但 '£' 字符看起来是错误的。

任何指针?

4

1 回答 1

14

这是Tilt中的一个问题,这是Sinatra 使用的模板系统(并且正在考虑用于 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' %>
£

通过使用read而不是自己读取文件binread,您可以确保它具有正确的编码,因此与文件的其余部分兼容erb。如果您尝试这样做,您可能希望一次读取文件并将内容缓存在某处。

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

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

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

于 2012-04-27T01:50:53.297 回答