Rails 导入导出CSV数据时的中文编码问题

Posted by devon on December 12th, 2008 filed in Rails应用

在Rails中导入导出数据到 Excel 时,使用CSV格式较多,但在转换各种编码时,经常出现个别字符的乱码问题,主要原因在于 utf8, gb2321, gb18030, gbk 的字符集所包含的中文字不一样,在实践中,总结出来如下的办法来做导入导出。由于在实际应用中,以 utf8 为主,这里也以 utf8 为核心来做导入导出。

导出数据

将 utf8 转为 unicode 编码,在excel 2003, 及excel 2007 中都能正确识别中文字符,而且 unicode 编码字符集大,基本无乱码现象。导出后的 csv 文件需要加BOM标记。

示例代码:
1, 在String类中增加一些转换字符集的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class String
  def utf8_to_gb2321
    encode_convert(self, "gb2321", "UTF-8")
  end
 
  def gb2321_to_utf8
    encode_convert(self, "UTF-8", "gb2321")
  end
 
  def utf8_to_utf16
    encode_convert(self, "UTF-16LE", "UTF-8")
  end
 
  def utf8?
    begin
      utf8_arr = self.unpack('U*')
      true if utf8_arr && utf8_arr.size > 0
    rescue
      false
    end
  end
 
  private
  def encode_convert(s, to, from)
    require 'iconv'
    begin
      converter = Iconv.new(to, from)
      converter.iconv(s)
    rescue
      s
    end
  end
end

2, 如果直接导出CSV文件,并发送到客户端,参考如下代码:

1
2
3
4
5
6
7
8
9
10
11
def export_csv
  require 'fastercsv'
  records = Amodel.find(:all, :limit => 10)
  csv_string = FasterCSV.generate(:col_sep => "\t", :row_sep => "\r\n") do |csv|
    csv << ['field_name1', 'field_name2', 'field_name3']
    records.each do |r|
      csv << [r.field1, r.field2, r.field3]
    end
  end
  send_data("FFFE".gsub(/\s/,'').to_a.pack("H*") + csv_string.utf8_to_utf16, :type => 'text/csv; charset=utf16; header=present', :filename => "exp.csv")   
end

这里需要注意几点:a), 值之间使用\t标记(:col_sep),而不是通常的逗号。 b) FFFE是utf_16le的BOM标记,写在文件头,表示该文件是utf_16le编码。

3,如果直接写到csv文件(比如在一些Rake Task中),参考如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private 
def export_csv
  require 'fastercsv'
  records = Amodel.find(:all, :limit => 10)
  csv_string = FasterCSV.generate(:col_sep => "\t", :row_sep => "\r\n") do |csv|
    csv << ['field_name1', 'field_name2', 'field_name3']
    records.each do |r|
      csv << [r.field1, r.field2, r.field3]
    end
  end
 
  File.open("export.csv","w") do |file|
    file.syswrite "FFFE".gsub(/\s/,'').to_a.pack("H*") + csv_string.utf8_to_utf16
  end 
end

导入数据

从CSV导入数据时,要注意编码的检测,通常情况下,用户的原始文件会以 GB2321 形式存在,这时,只需要将导入的数据从 GB2321 转换为 UTF8 即可。如果原始文件已经是 UTF8,就不再需要转换了。

1
2
3
4
5
6
7
8
def import_to_db
  rows = CSV::parse(File.open("input_file.csv") {|f| f.read})
  is_utf8 = rows[0].join(',').utf8?
  rows.each { |row|
    row = row.map {|v| v.gb2321_to_utf8} unless is_utf8
    Amodel.create(:filed1 => row[0], :field2 => row[2], :field3 => row[3])
  end
end

在转换前,探测一下文件的第一行数据是不是UTF8,如果已经是UTF8,就不再需要转换了。

扩展阅读:
GB18030, GBK, GB2321: 最早制定的汉字编码是GB2312,包括6763个汉字和682个其它符号 95年重新修订了编码,命名GBK1.0,共收录了21886个符号。 之后又推出了GBK18030编码,共收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字,现在WINDOWS平台必需要支持GBK18030编码。按照GBK18030、GBK、GB2312的顺序,3种编码是向下兼容,同一个汉字在三个编码方案中是相同的编码。

BOM: Byte Order Mark,就是字节序标记。参考官方解释

Related posts:

  1. 利用FastCSV将网站数据导出到csv文件 安装: sudo gem install fastercsv 具体使用看Readme,下面介绍FastCSV在导出网站数据中的应用 主函数代码如下,在关键点已经给出注释。 def export_csv(showfieldname, showfield,...

Related posts brought to you by Yet Another Related Posts Plugin.


One Response to “Rails 导入导出CSV数据时的中文编码问题”

  1. hlxwell Says:

    thanks devon.
    nice tutorial.

    [Reply]

Leave a Comment