Ruby Logo

Character Encoding

Master character encoding in Ruby including UTF-8, ASCII, encoding conversion, and handling encoding issues in files and web applications.

Home Ruby Character Encoding

Character Encoding in Ruby

Character encoding is fundamental to handling text in Ruby. Understanding UTF-8, ASCII, and encoding issues is crucial for building robust international applications and preventing encoding-related bugs.

Understanding Character Encoding

Character encoding defines how computers represent text characters as bytes. Ruby has excellent built-in support for various encodings, with UTF-8 being the default and most commonly used.

Common Encodings in Ruby

# Check available encodings
puts Encoding.list.size  # Shows all available encodings

# Common encodings
Encoding::UTF_8          # Unicode (default)
Encoding::ASCII          # Basic ASCII
Encoding::ISO_8859_1     # Latin-1
Encoding::Windows_1252   # Windows encoding
Encoding::BINARY         # Binary data

# Check string encoding
str = "Hello"
puts str.encoding        # => #<Encoding:UTF-8>
puts str.encoding.name   # => "UTF-8"

Working with Different Encodings

Ruby provides powerful methods to work with and convert between different character encodings.

Creating Strings with Specific Encodings

# Create strings with specific encodings
utf8_string = "Hello 世界".encode(Encoding::UTF_8)
ascii_string = "Hello".encode(Encoding::ASCII)
binary_string = "\xFF\xFE".force_encoding(Encoding::BINARY)

# Check encoding compatibility
puts utf8_string.encoding.name    # => "UTF-8"
puts ascii_string.encoding.name   # => "US-ASCII"
puts binary_string.encoding.name  # => "ASCII-8BIT"

# String literals with encoding comments
# encoding: utf-8
japanese = "こんにちは"

# encoding: ascii-8bit
binary_data = "\x00\x01\x02"

Encoding Conversion and Validation

Convert between encodings and handle encoding issues safely with Ruby's built-in methods.

Safe Encoding Conversion

# Convert between encodings
utf8_text = "Hello 世界"
ascii_text = utf8_text.encode(Encoding::ASCII,
                              invalid: :replace,
                              undef: :replace,
                              replace: '?')
puts ascii_text  # => "Hello ??"

# Check if string is valid in its encoding
def valid_encoding?(str)
  str.valid_encoding?
end

# Force encoding (dangerous - use carefully)
binary_data = "\xFF\xFE"
forced_utf8 = binary_data.force_encoding(Encoding::UTF_8)
puts forced_utf8.valid_encoding?  # => false

# Safe encoding detection and conversion
def safe_encode_to_utf8(str)
  if str.valid_encoding?
    str.encode(Encoding::UTF_8)
  else
    str.encode(Encoding::UTF_8,
               invalid: :replace,
               undef: :replace,
               replace: '�')
  end
end

File I/O and Encoding

Handle file encoding properly when reading and writing files to prevent data corruption.

Reading Files with Specific Encodings

# Read file with specific encoding
content = File.read('data.txt', encoding: 'UTF-8')

# Read with encoding conversion
content = File.read('legacy.txt',
                   encoding: 'ISO-8859-1:UTF-8')

# Write with specific encoding
File.write('output.txt', "Hello 世界", encoding: 'UTF-8')

# Handle encoding errors when reading
def safe_read_file(filename)
  begin
    File.read(filename, encoding: 'UTF-8')
  rescue Encoding::InvalidByteSequenceError
    # Fallback to binary read and convert
    binary_content = File.read(filename, encoding: 'BINARY')
    binary_content.encode('UTF-8',
                         invalid: :replace,
                         undef: :replace)
  end
end

# Open file with specific encoding
File.open('data.txt', 'r:UTF-8') do |file|
  file.each_line do |line|
    puts "#{line.encoding.name}: #{line}"
  end
end

Common Encoding Issues and Solutions

Learn to identify and fix common encoding problems in Ruby applications.

Debugging Encoding Issues

# Encoding incompatibility error
begin
  ascii_str = "Hello".encode(Encoding::ASCII)
  utf8_str = "世界".encode(Encoding::UTF_8)
  result = ascii_str + utf8_str  # This works (ASCII is subset of UTF-8)
rescue Encoding::CompatibilityError => e
  puts "Encoding error: #{e.message}"
end

# Force compatible encodings
def force_compatible(str1, str2)
  if str1.encoding != str2.encoding
    # Convert both to UTF-8
    str1 = str1.encode(Encoding::UTF_8)
    str2 = str2.encode(Encoding::UTF_8)
  end
  str1 + str2
end

# Detect encoding issues
def diagnose_string(str)
  puts "String: #{str.inspect}"
  puts "Encoding: #{str.encoding.name}"
  puts "Valid: #{str.valid_encoding?}"
  puts "Bytesize: #{str.bytesize}"
  puts "Length: #{str.length}"
  puts "Bytes: #{str.bytes.map { |b| "0x%02X" % b }.join(' ')}"
end

# Example of encoding detection
suspect_string = "\xFF\xFE\x48\x00\x65\x00\x6C\x00\x6C\x00\x6F\x00"
diagnose_string(suspect_string)

# Try different encodings to find the right one
encodings_to_try = [
  Encoding::UTF_16LE,
  Encoding::UTF_16BE,
  Encoding::UTF_8,
  Encoding::ISO_8859_1
]

encodings_to_try.each do |encoding|
  begin
    decoded = suspect_string.force_encoding(encoding)
    if decoded.valid_encoding?
      puts "Possible encoding: #{encoding.name} => #{decoded.inspect}"
    end
  rescue
    # Skip invalid encodings
  end
end

Web Application Encoding

Handle encoding properly in web applications to ensure correct display and data processing.

HTTP and Encoding

# Set default external encoding
Encoding.default_external = Encoding::UTF_8

# Handle form data encoding
def process_form_data(params)
  params.each do |key, value|
    if value.is_a?(String)
      # Ensure UTF-8 encoding for web data
      value.force_encoding(Encoding::UTF_8)
      unless value.valid_encoding?
        value = value.encode(Encoding::UTF_8,
                            invalid: :replace,
                            undef: :replace,
                            replace: '�')
      end
    end
  end
end

# Database encoding considerations
class User
  def self.safe_create(attributes)
    # Ensure all string attributes are properly encoded
    safe_attributes = attributes.map do |key, value|
      if value.is_a?(String)
        safe_value = value.encode(Encoding::UTF_8,
                                 invalid: :replace,
                                 undef: :replace)
        [key, safe_value]
      else
        [key, value]
      end
    end.to_h

    create(safe_attributes)
  end
end

# JSON encoding
require 'json'

# JSON is always UTF-8
data = { name: "Hello 世界", emoji: "🌟" }
json_string = JSON.generate(data)
puts json_string.encoding.name  # => "UTF-8"

# Parse JSON with encoding awareness
json_data = '{"name": "Hello 世界"}'
parsed = JSON.parse(json_data)
puts parsed["name"].encoding.name  # => "UTF-8"

Best Practices

Encoding Best Practices

  • Use UTF-8 as your default encoding for new projects
  • Always validate encoding when reading external data
  • Handle errors gracefully with replacement characters
  • Set encoding explicitly when reading files from unknown sources
  • Test with international characters during development
  • Use force_encoding carefully - only when you know the actual encoding
  • Document encoding expectations in your APIs and file formats

Advanced Encoding Techniques

Advanced techniques for handling complex encoding scenarios and performance optimization.

Encoding Utilities

# Create an encoding utility class
class EncodingHelper
  def self.detect_encoding(data)
    # Simple heuristic encoding detection
    return Encoding::UTF_8 if data.valid_encoding?

    # Try common encodings
    [Encoding::ISO_8859_1, Encoding::Windows_1252].each do |enc|
      test = data.dup.force_encoding(enc)
      return enc if test.valid_encoding?
    end

    Encoding::BINARY
  end

  def self.normalize_to_utf8(str)
    case str.encoding
    when Encoding::UTF_8
      str.valid_encoding? ? str : repair_utf8(str)
    when Encoding::ASCII, Encoding::US_ASCII
      str.encode(Encoding::UTF_8)
    else
      str.encode(Encoding::UTF_8,
                invalid: :replace,
                undef: :replace,
                replace: '�')
    end
  end

  private

  def self.repair_utf8(str)
    str.encode(Encoding::UTF_8,
              invalid: :replace,
              undef: :replace,
              replace: '�')
  end
end

# Performance considerations
def fast_ascii_check(str)
  # Quick check for ASCII-only content
  str.ascii_only?
end

# Memory-efficient encoding conversion for large files
def convert_large_file(input_file, output_file, from_enc, to_enc)
  File.open(output_file, 'w', encoding: to_enc) do |output|
    File.foreach(input_file, encoding: from_enc) do |line|
      output.puts line.encode(to_enc,
                             invalid: :replace,
                             undef: :replace)
    end
  end
end

💡 Pro Tips

  • Set # encoding: utf-8 at the top of Ruby files when using non-ASCII characters
  • Use String#scrub method to clean up invalid byte sequences
  • Consider using Encoding::Converter for complex encoding transformations
  • Test your application with various international inputs early in development
  • Be aware that String#length returns character count, not byte count

🚀 Next Steps

Now that you understand character encoding, explore Unicode normalization and internationalization features to build truly global applications.

Quick Navigation

Related Topics

Video Tutorial

Watch and learn character encoding

Pro Tip: After reading through the content above, watch this video to reinforce your understanding and see the concepts in action!