csv文件中的utf8字符

Seb*_*Seb 1 ruby csv utf-8 mongodb

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

extract_data类:

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

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

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

{
       "zip" => "71964",
         "cbsa" => "31680",
    "res_ratio" => "0.086511098",
    "bus_ratio" => "0.012048193",
    "oth_ratio" => "0.000000000",
    "tot_ratio" => "0.082435345"
}
Run Code Online (Sandbox Code Playgroud)

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

我也试过了:

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

但同样的事情

ArgumentError - invalid byte sequence in UTF-8
Run Code Online (Sandbox Code Playgroud)

这是文件csv文件

谢谢

7st*_*tud 6

如果我从csv文件中读出你读到的单词:

 zip
Run Code Online (Sandbox Code Playgroud)

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

                   z  i  p
                   |  |  |
                   V  V  V
C3 AF C2 BB C2 BF 7A 69 70
Run Code Online (Sandbox Code Playgroud)

那么"拉链"面前的垃圾是什么?

UTF-8 BOM是字符串:

"\xEF\xBB\xBF"
Run Code Online (Sandbox Code Playgroud)

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

"\xEF\xBB\xBF".force_encoding("ISO-8859-1")
Run Code Online (Sandbox Code Playgroud)

然后看一下这些十六进制代码的iso-8859-1图表,我发现:

EF  => ï
BB  => »
BF  => ¿
Run Code Online (Sandbox Code Playgroud)

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

"\xEF\xBB\xBF".force_encoding("ISO-8859-1").encode("UTF-8")
Run Code Online (Sandbox Code Playgroud)

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

ï   c3 af   LATIN SMALL LETTER I WITH DIAERESIS
»   c2 bb   RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
¿   c2 bf   INVERTED QUESTION MARK
Run Code Online (Sandbox Code Playgroud)

给我:

"\xC3\xAF\xC2\xBB\xC2\xBF"
Run Code Online (Sandbox Code Playgroud)

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

C3 AF C2 BB C2 BF
Run Code Online (Sandbox Code Playgroud)

与十六进制编辑器显示的内容相比:

                   z  i  p
                   |  |  |
                   V  V  V
C3 AF C2 BB C2 BF 7A 69 70
Run Code Online (Sandbox Code Playgroud)

看起来熟悉?

当你写这篇文章时,你要求ruby做与上面相同的事情:

 CSV.foreach(file_path, encoding: 'iso-8859-1:UTF-8')
Run Code Online (Sandbox Code Playgroud)

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

 "\xEF\xBB\xBF"
Run Code Online (Sandbox Code Playgroud)

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

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

因此ruby经历了与上述相同的过程,在ruby程序中生成一个如下所示的字符串:

"\xC3\xAF\xC2\xBB\xC2\xBF"
Run Code Online (Sandbox Code Playgroud)

这会搞砸你的第一行CSV数据.你说:

我正在使用编码来确保只处理UTF8字符

但这对我没有任何意义.如果文件是UTF-8,那么告诉ruby外部编码是UTF-8:

                               external encoding 
                                     |
                                     V
CSV.foreach(file_path, encoding: 'UTF-8:UTF-8')
                                           ^
                                           |
                                        internal encoding
Run Code Online (Sandbox Code Playgroud)

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

该编码可以正常工作CSV.foreach(),并且在CSV确定文件的编码后,它将导致CSV跳过BOM.

回复评论:

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

"Doña Ana County"
Run Code Online (Sandbox Code Playgroud)

该字符在文件中ñ指定F1,即字符的ISO-8559-1十六进​​制代码ñ,并且没有随机UTF-8字符和编码F1(在UTF-8中,带有TILDE的LATIN SMALL LETTER N的十六进制代码实际上是C3 B1).

如果将外部编码更改为"ISO-8859-1"并将内部编码指定为"UTF-8",则CSV将处理该文件而不会出现错误,CSV会将F1读取的文件转换为C3 B1并传送程序UTF-8编码的字符串. 底线是:您必须知道要读取的文件的编码.如果您正在阅读许多文件并且它们都有不同的编码,那么您必须先了解每个文件的编码,然后才能阅读它们.如果您确定所有文件都是ISO-8859-1或UTF-8,那么您可以尝试使用一种编码读取文件,如果CSV错误输出,您可以捕获编码错误并尝试其他编码.