繁体   English   中英

Rails ActiveRecord字符串字段编码与Ruby String编码

[英]Rails ActiveRecord string field encoding vs Ruby String encoding

上下文:从外部源代码转换字符串以保存在数据库中

从宝石,我得到一个字符串slatin-1编码过的内容和我想在Rails模型来存储。

r = MyRecord.new(mystring: s)
# ...
r.save

因为我的PostgreSQL数据库使用UTF-8编码,所以在将字符串字段设置为字符串后保存模型会在该字符串包含某些非ASCII字符时导致错误:

ActiveRecord::StatementInvalid: PG::CharacterNotInRepertoire: ERROR:  invalid byte sequence for encoding "UTF8": 0xdf 0x65
...

我可以通过转码字符串轻松解决这个问题:

r = MyRecord.new(mystring: s.encode(Encoding::UTF_8, Encoding::ISO_8859_1))
# ...
r.save

(因为r.encoding返回#<Encoding:ASCII-8BIT>而不是#<Encoding:ISO-8859-1> ,我将源编码作为第二个参数传递 。生成s的gem可能不知道它从字符串读取的文件是latin1编码。)

挑战:避免硬编码目标编码

在我看来,关于数据库的字符串编码的知识不属于我持久化的代码部分,因此也不属于代码转换。

我可以向模型的类询问数据库的编码:

MyRecord.connection.encoding

但这不会返回Ruby Encoding对象,它返回一个包含编码名称的字符串。 幸运的是, 可以使用名称 (和一些别名查询 Encoding类以查找编码:

Encoding.find 'UTF-8' # returns #<Encoding:UTF-8>, the value of Encoding::UTF_8

不幸的是,使用了不同的命名约定: MyRecord.connection.encoding返回'UTF8'没有减号),而Encoding.find(...)需要传递'UTF-8'减号)或'CP65001'如果我们希望它返回#<Encoding:UTF-8> 。)

Sooooo关闭。

问题:是否有干净和/或推荐的方式

避免目标编码的硬编码,而是动态地确定和使用数据库的编码?

丢弃的想法

我不觉得对MyRecord.connection.encoding的结果进行字符串操作或模式匹配,或者对Encoding.aliases()的内容进行模式匹配比将代码中的硬编码值保留更好。

修改Encoding.aliases()的返回值没有任何影响:

Encoding.aliases['UTF8'] = 'UTF-8'
Encoding.find 'UTF8' # ArgumentError: unknown encoding name - UTF8

(无论如何也感觉不对),也没有修改#names的返回值:

Encoding::UTF_8.names.push('UTF8')
Encoding.find 'UTF8'# ArgumentError: unknown encoding name - UTF8

我猜两者都只返回动态生成的集合或底层集合的副本,这是有充分理由的。

解决这个问题的最简单,也可以说是最干净的解决方案是不直接调用Encoding.find ,但是有一个实用工具方法(可能位于lib/yourapp的模块中),它知道你关心的编码名称差异并退回到所有其他输入的Encoding.find

module YourApp
  module DatabaseStringEncoding
    def find(name)
      case name
      when 'UTF8'
        Encoding::UTF_8
      ...
      else
        Encoding.find(name)
      end 
    end
  end

这很容易理解和发现(而不是直接修改Encoding ,这对编码的代码的读者来说是不可见的)。 基于这样的find方法,您可以进一步实现一个方法,该方法使用YourRecord.connection.encoding自动将字符串重新编码为数据库的字符串编码。

我知道让Encoding.find做你想要的更令人兴奋,但我认为这种“笨拙”的方法实际上会更好。 :-)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM