Ruby 2.6.3.
J'ai essayé d'analyser un objet StringIO dans une instance CSV avec le codage bom|utf-8 , afin que le caractère BOM (indésirable) soit supprimé et que le contenu soit codé en UTF-8:
require 'csv'
CSV_READ_OPTIONS = { headers: true, encoding: 'bom|utf-8' }.freeze
# File content is: "\xEF\xBB\xBFid\n12"
first_row = CSV.read('bom_content.csv', CSV_READ_OPTIONS).first
first_row.headers.first.include?("\xEF\xBB\xBF") # This returns false
Apparemment, l'encodage bom|utf-8 ne fonctionne pas pour les objets StringIO , mais j'ai trouvé qu'il fonctionne pour les fichiers, par exemple:
require 'csv'
CSV_READ_OPTIONS = { headers: true, encoding: 'bom|utf-8' }.freeze
content = StringIO.new("\xEF\xBB\xBFid\n123")
first_row = CSV.parse(content, CSV_READ_OPTIONS).first
first_row.headers.first.include?("\xEF\xBB\xBF") # This returns true
Étant donné que je dois travailler directement avec StringIO , pourquoi CSV ignore bom|utf-8 encodage bom|utf-8 ? Existe-t-il un moyen de supprimer le caractère BOM de l'instance StringIO ?
Merci!
3 Réponses :
Ruby n'aime pas les nomenclatures. Il ne les gère que lors de la lecture d'un fichier , jamais ailleurs, et même dans ce cas, il ne les lit que pour pouvoir s'en débarrasser. Si vous voulez une nomenclature pour votre chaîne ou une nomenclature lors de l'écriture d'un fichier, vous devez la gérer manuellement.
Il y a probablement des gemmes pour faire cela, même si c'est facile à faire soi-même
if string[0...3] == "\xef\xbb\xbf"
string = string[3..-1].force_encoding('UTF-8')
elsif string[0...2] == "\xff\xfe"
string = string[2..-1].force_encoding('UTF-16LE')
# etc
J'ai découvert que forcer le codage à utf8 sur la string StringIO et supprimer la nomenclature pour générer un nouveau StringIO fonctionnait:
require 'csv'
CSV_READ_OPTIONS = { headers: true}.freeze
content = StringIO.new("\xEF\xBB\xBFid\n123")
csv_file = StringIO.new(content.string.force_encoding('utf-8').sub("\xEF\xBB\xBF", ''))
first_row = CSV.parse(csv_file, CSV_READ_OPTIONS).first
first_row.headers.first.include?("\xEF\xBB\xBF") # => false
L'option d' encoding n'est plus nécessaire. Ce n'est peut-être pas la meilleure option en termes de mémoire, mais cela fonctionne.
Ruby 2.7 a ajouté la méthode set_encoding_by_bom à IO . Cette méthode utilise la marque d'ordre des octets et définit le codage.
require 'csv'
require 'stringio'
CSV_READ_OPTIONS = { headers: true }.freeze
content = StringIO.new("\xEF\xBB\xBFid\n123")
content.set_encoding_by_bom
first_row = CSV.parse(content, CSV_READ_OPTIONS).first
first_row.headers.first.include?("\xEF\xBB\xBF")
#=> false
N'est-il pas possible de supprimer la nomenclature avant de créer l'instance StringIO ou d'en créer une autre basée sur une chaîne UTF-8 sans nomenclature? Toutes les versions de StringIO publiées ne prennent pas en charge la gestion des nomenclatures.
Le problème est que (depuis Ruby 2.4) la nomenclature est une propriété de fichiers , pas un encodage. Si vous disposez déjà d'une chaîne codée, la nomenclature n'existe pas, car les caractères ont déjà été correctement lus conformément à la nomenclature et elle n'est plus nécessaire. Puisque StringIO est soutenu par une chaîne - pas un fichier - il ne comprend pas non plus la nomenclature.