Chances are, some of you have run into the issue with the invalid byte sequence in UTF-8 error when dealing with user-submitted data. AGoogle search shows that my hunch isn’t off.

Among the search results are plenty of answers—some using the deprecated iconv library—that might lead you to a sufficient fix. However, among the slew of queries are few answers on how to reliably replicate and test the issue.

In developing the Griddler gem we ran into some cases where the data being posted back to our controller had invalid UTF-8 bytes. For Griddler, our failing case needs to simulate the body of an email having an invalid byte, and encoded as UTF-8.

What are valid and invalid bytes? This table on Wikipedia tells us bytes 192, 193, and 245-255 are off limits. In ruby’s string literal we can represent this by escaping one of those numbers:

> "hi \255"
=> "hi \xAD"

There’s our string with the invalid byte! How do we know for sure? In that IRB session we can simulate a comparable issue by sending a message to the string it won’t like - like split or gsub.

> "hi \255".split(' ')
ArgumentError: invalid byte sequence in UTF-8
from (irb):9:in `split'
from (irb):9
from /Users/joel/.rvm/rubies/ruby-1.9.3-p125/bin/irb:16:in `<main>'

Yup. It certainly does not like that.

Let’s create a very real-world, enterprise-level, business-critical test case:

invalid_byte_spec.rb

require 'rspec'

def replace_name(body, name)
body.gsub(/joel/, name)
end describe 'replace_name' do
it 'removes my name' do
body = "hello joel" replace_name(body, 'hank').should eq "hello hank"
end it 'clears out invalid UTF-8 bytes' do
body = "hello joel\255" replace_name(body, 'hank').should eq "hello hank"
end
end

The first test passes as expected, and the second will fail as expected but not with the error we want. By adding that extra byte we should see an exception raised similar to what we simulated in IRB. Instead it’s failing in the comparison with the expected value.

1) replace_name clears out invalid UTF-8 bytes
Failure/Error: replace_name(body, 'hank').should eq "hello hank" expected: "hello hank"
got: "hello hank\xAD" (compared using ==)
# ./invalid_byte_spec.rb:17:in `block (2 levels) in <top (required)>'

Why isn’t it failing properly? If we pry into our running test we find out that inside our file the strings being passed around are encoded as ASCII-8BIT instead of UTF-8.

[2] pry(#<RSpec::Core::ExampleGroup::Nested_1>)> body.encoding
=> #<Encoding:ASCII-8BIT>

As a result we’ll have to force that string’s encoding to UTF-8:

it 'clears out invalid UTF-8 bytes' do
body = "hello joel\255".force_encoding('UTF-8') replace_name(body, 'hank').should_not raise_error(ArgumentError)
replace_name(body, 'hank').should eq "hello hank"
end

By running the test now we will see our desired exception

1) replace_name clears out invalid UTF-8 bytes
Failure/Error: body.gsub(/joel/, name)
ArgumentError:
invalid byte sequence in UTF-8
# ./invalid_byte_spec.rb:4:in `gsub'
# ./invalid_byte_spec.rb:4:in `replace_name'
# ./invalid_byte_spec.rb:17:in `block (2 levels) in <top (required)>' Finished in 0.00426 seconds
2 examples, 1 failure

Now that we’re comfortably in the red part of red/green/refactor we can move on to getting this passing by updating our replace_name method.

def replace_name(body, name)
body
.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
.gsub(/joel/, name)
end

And the test?

Finished in 0.04252 seconds
2 examples, 0 failures

For such a small piece of code we admittedly had to jump through some hoops. Through that process, however, we learned a bit about character encoding and how to put ourselves in the right position—through the red/green/refactor cycle—to fix bugs we will undoubtedly run into while writing software.

#encoding: utf-8
require 'json'
f="dsp-cpi"
File.open(f).each do |line|
line = line.encode("UTF-16be", :invalid=>:replace, :replace=>"?").encode('UTF-8')
end

UTF-8 Invalid Byte Sequences的更多相关文章

  1. maven filter 乱码,MalformedByteSequenceException: Invalid byte 3 of 3-byte UTF-8 sequence.

    <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactI ...

  2. MalformedByteSequenceException: Invalid byte 1 of 1-byte

    修改了线上程序的xml配置文件,重启后报如下错误: MalformedByteSequenceException: Invalid byte 1 of 1-byte 百度了下大体的意思是说文件的编码错 ...

  3. [字符编码]Invalid byte 1 of 1-byte UTF-8 sequence终极解决方案

    今天在eclipse中编写pom.xml文件时,注释中的中文被eclipse识别到错误:Invalid byte 1 of 1-byte UTF-8 sequence,曾多次遇到该问题,问题的根源是: ...

  4. 读取xml文件报错:Invalid byte 2 of 2-byte UTF-8 sequence。

    程序读取xml文件后,系统报“Invalid byte 2 of 2-byte UTF-8 sequence”错误,如何解决呢? 1.程序解析xml的时候,出现Invalid byte 2 of 2- ...

  5. Invalid byte 3 of 3-byte UTF-8 sequence

    用maven编译,tomcat启动时报错:IOException parsing XML document from class path resource [applicationContext.x ...

  6. com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 2 of 2-byte

    com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 2 of 2-byte ...

  7. tomcat部署新的项目的时候出现报错信息: Invalid byte tag in constant pool: 15

    上面一堆tomcat启动的提示信息省略掉,下面是报错的具体信息:org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid ...

  8. xml中1字节的UTF-8序列的字节1无效([字符编码]Invalid byte 1 of 1-byte UTF-8 sequence终极解决方案)

    今天在eclipse中编写pom.xml文件时,注释中的中文被eclipse识别到错误:Invalid byte 1 of 1-byte UTF-8 sequence,曾多次遇到该问题,问题的根源是: ...

  9. Xml读取异常--Invalid byte 1 of 1-byte UTF-8 sequence

    xml读取异常Invalid byte 1 of 1-byte UTF-8 sequence org.dom4j.DocumentException: Invalid byte 1 of 1-byte ...

随机推荐

  1. 【资料搜集】Python学习

    python学习手册 | 演道网 http://dev.go2live.cn/python/python%e5%ad%a6%e4%b9%a0%e6%89%8b%e5%86%8c.html

  2. java Calendar类得到每个月的周末是几号的工具方法

    public static List getWeekendInMonth(int year, int month) { List list = new ArrayList(); Calendar ca ...

  3. pytorch加载和保存模型

    在模型完成训练后,我们需要将训练好的模型保存为一个文件供测试使用,或者因为一些原因我们需要继续之前的状态训练之前保存的模型,那么如何在PyTorch中保存和恢复模型呢? 方法一(推荐): 第一种方法也 ...

  4. LeetCode 43 字符串相乘

    题目: 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式. 示例 1: 输入: num1 = "2", ...

  5. AntPathMatcher做路径匹配

    转发自: http://www.cnblogs.com/leftthen/p/5212221.html 需要看详细的请看上面的链接 这里以我这里的一个Filter 中需要对路径做例外处理,filter ...

  6. Codeforces Round #506 (Div. 3) C. Maximal Intersection

    C. Maximal Intersection time limit per test 3 seconds memory limit per test 256 megabytes input stan ...

  7. L323 英语有必要学语法吗

    The Agony and Ecstasy of Grammar “Underline a relative clause.” This challenge would give a lot of a ...

  8. 识别图片验证码的三种方式(scrapy模拟登陆豆瓣网)

    1.通过肉眼识别,然后输入到input里面 from PIL import image Image request.urlretrieve(url,'image')  #下载验证码图片 image = ...

  9. Tomcat9配置SSL连接

    .首先生成数字证书: 使用JDK的keytool命令,生成证书(包含证书/公钥/私钥)到D:\ssl.keystore:keytool -genkey -keystore "D:\ssl.k ...

  10. Non-technical Blog Recording on Day of Sep. 19th 2017 in Retrospection.

    Unfortunately, I heard a resignation message verbally from ESU (her name is: Su Yi in Chinese, Su fr ...