ruby - csv文件中的utf8字符

标签 ruby mongodb csv utf-8

我正在尝试使用 mongoid 创建数据库,但 Mongo 正在创建数据库,我在使用 utf8 编码数据库时遇到了问题

提取数据类:

class ExtractData

  include Mongoid::Document
  include Mongoid::Timestamps

  def self.create_all_databases
    @cbsa2msa = DbForCsv.import!('./share/private/csv/cbsa_to_msa.csv')
    @zip2cbsa = DbForCsv.import!('./share/private/csv/zip_to_cbsa.csv')
  end

  def self.show_all_database
    ap @cbsa2msa.all.to_a
    ap @zip2cbsa.all.to_a
  end

end

类 DbForCSV 的工作方式如下:

class DbForCsv
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::Attributes::Dynamic

  def self.import!(file_path)
    columns = []
    instances = []
    CSV.foreach(file_path, encoding: 'iso-8859-1:UTF-8') do |row|
      if columns.empty?
        # We dont want attributes with whitespaces
        columns = row.collect { |c| c.downcase.gsub(' ', '_') }
        next
      end
      instances << create!(build_attributes(row, columns))
    end
    instances
  end

  private

  def self.build_attributes(row, columns)
    attrs = {}
    columns.each_with_index do |column, index|
      attrs[column] = row[index]
    end
    ap attrs
    attrs
  end
end

我正在使用编码来确保只处理 UTF8 字符,但我仍然看到:

{
       "zip" => "71964",
         "cbsa" => "31680",
    "res_ratio" => "0.086511098",
    "bus_ratio" => "0.012048193",
    "oth_ratio" => "0.000000000",
    "tot_ratio" => "0.082435345"
}

在代码中执行“ap attrs”时。如何确保 'zip' -> 'zip'

我也试过:

    columns = row.collect { |c| c.encode(Encoding.find('UTF-8'), {invalid: :replace, undef: :replace, replace: ''}).downcase.gsub(' ', '_')}

同样的事情

ArgumentError - invalid byte sequence in UTF-8

这是文件 csv file

谢谢

最佳答案

如果我从 csv 文件中获取您读入的单词:

 zip

并将其粘贴到十六进制编辑器中,它显示该单词由字节组成:

                   z  i  p
                   |  |  |
                   V  V  V
C3 AF C2 BB C2 BF 7A 69 70

那么“zip”前面的垃圾是什么?

UTF-8 BOM 是字符串:

"\xEF\xBB\xBF"

如果我强制将 BOM 字符串(在 ruby​​ 程序中默认为 UTF-8)编码为 iso-8859-1:

"\xEF\xBB\xBF".force_encoding("ISO-8859-1")

然后看一个iso-8859-1 chart对于那些十六进制代码,我发现:

EF  => ï
BB  => »
BF  => ¿

接下来,如果我将 BOM 字符串编码为 UTF-8:

"\xEF\xBB\xBF".force_encoding("ISO-8859-1").encode("UTF-8")

要求 ruby​​ 将字符串中的十六进制转义替换为 UTF-8 编码中相同字符的十六进制转义,它们是:

ï   c3 af   LATIN SMALL LETTER I WITH DIAERESIS
»   c2 bb   RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
¿   c2 bf   INVERTED QUESTION MARK

给我:

"\xC3\xAF\xC2\xBB\xC2\xBF"

删除 ruby​​ 的十六进制转义语法给我:

C3 AF C2 BB C2 BF

将其与十六进制编辑器显示的内容进行比较:

                   z  i  p
                   |  |  |
                   V  V  V
C3 AF C2 BB C2 BF 7A 69 70

看起来很眼熟?

当你这样写时,你是在要求 ruby​​ 做与上面相同的事情:

 CSV.foreach(file_path, encoding: 'iso-8859-1:UTF-8')

在文件中,文件开头有一个 UTF-8 BOM:

 "\xEF\xBB\xBF"

但是,您告诉 ruby​​ 该文件是用 ISO-8859-1 编码的,并且您希望 ruby​​ 在您的 ruby​​ 程序中将文件转换为 UTF-8 字符串:

                               external encoding 
                                     |
                                     V
CSV.foreach(file_path, encoding: 'iso-8859-1:UTF-8')
                                               ^
                                               |
                                        internal encoding

因此,ruby 通过与上述相同的过程在您的 ruby​​ 程序中生成如下所示的字符串:

"\xC3\xAF\xC2\xBB\xC2\xBF"

这搞砸了您的第一行 CSV 数据。你说:

I am using the encoding to make sure only UTF8 char are handled

但这对我来说没有任何意义。如果文件是 UTF-8,则告诉 ruby​​ 外部编码是 UTF-8:

                               external encoding 
                                     |
                                     V
CSV.foreach(file_path, encoding: 'UTF-8:UTF-8')
                                           ^
                                           |
                                        internal encoding

Ruby 在读取文件时不会自动跳过 BOM,因此您仍然会在第一行的开头看到有趣的字符。要解决此问题,您可以使用外部编码 'BOM|UTF-8' ,它告诉 ruby​​ 使用 BOM(如果存在)来确定外部编码,然后跳过 BOM;或者如果不存在 BOM,则使用“UTF-8”作为外部编码:

                               external encoding 
                                     |
                                     V
CSV.foreach(file_path, encoding: 'BOM|UTF-8:UTF-8')
                                             ^
                                             |
                                          internal encoding

该编码适用于 CSV.foreach() , 它会导致 CSV 在 CSV 确定文件的编码后跳过 BOM。

对评论的回应:

您发布的文件不是 UTF-8,也没有 BOM。当您将外部编码指定为 "BOM|UTF-8" 时并且没有 BOM,您告诉 CSV 退回到 UTF-8 的外部编码,并且 CSV 错误出现在这一行:

"Doña Ana County"

字符ñ指定为 F1在文件中,这是字符 ñ 的 ISO-8559-1 十六进制代码, 并且没有编码为 F1 的随机 UTF-8 字符(在 UTF-8 中,带有波浪线的拉丁文小写字母 N 的十六进制代码实际上是 C3 B1)。

如果将外部编码更改为“ISO-8859-1”并将内部编码指定为“UTF-8”,则 CSV 将无误地处理文件,CSV 将转换 F1从文件中读取到 C3 B1并把你的程序交给 UTF-8 编码的字符串。 底线是:你必须知道文件的编码才能读取它。如果你正在读取许多文件并且它们都有不同的编码,那么你必须在阅读之前知道每个文件的编码可以阅读它。如果您确定所有文件都是 ISO-8859-1 或 UTF-8,那么您可以尝试使用一种编码读取文件,如果 CSV 出错,您可以捕获编码错误并尝试另一种编码。

关于ruby - csv文件中的utf8字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43060250/

相关文章:

ruby-on-rails - 一个 Rails 应用程序中存在两个独立命名空间的 Gemfile?

ruby:以原始模式将击键发送到 PTY

ruby - 在 Compass 项目目录结构中使用 Sass partials 的子文件夹

node.js - 如何对 MongoDB 中数组内的字段值进行分组?

javascript - csv 数据在 d3 脚本内无法访问,但可以在控制台中访问

Python CSV 到 JSON W/数组输出

ruby-on-rails - Ruby 和 Null 字符的正则表达式匹配

node.js - MapReduce 中的 MongoDB 动态变量

c# - MongoDb - 使用 .NET 驱动程序运行 adminCommand (2.4.4)

Python 添加带标题的新列