我正在尝试使用 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/