rubyguides

String#encode

str.encode(encoding) -> string

String#encode converts the encoding of a string to a specified target encoding. It returns a new string with the same characters but re-encoded, leaving the original string unchanged. This method is essential for handling multi-language text, fixing encoding issues, and ensuring strings are compatible with specific protocols or storage systems.

Use it when bytes arrive in one encoding but the rest of your Ruby program expects another. It is especially useful at system boundaries: files, CSV imports, HTTP payloads, mail headers, legacy databases, and command-line tools. For unsafe input, combine encode with replacement options so invalid bytes become predictable characters instead of runtime exceptions.

The main idea is simple: encode changes how Ruby interprets or emits the text without changing the meaning you want to keep. If the source data is already valid, the result is a clean conversion. If the source data is messy, the options let you decide whether to raise an error or substitute a placeholder.

Syntax

string.encode(target_encoding)
string.encode(target_encoding, source_encoding)
string.encode(target_encoding, options)

Parameters

ParameterTypeDefaultDescription
target_encodingSymbolRequiredThe encoding to convert to (e.g., :UTF_8, :ISO-8859-1)
source_encodingSymbolnilThe current encoding of the string (Ruby attempts to detect if omitted)
optionsHash{}Conversion options like invalid: :replace, undef: :replace

Examples

Basic encoding conversion

string = "hello"
string.encoding
# => #<Encoding:UTF-8>

utf8_string = "hello"
utf8_string.encode(:ISO-8859-1)
# => "hello"
# Returns a new string in ISO-8859-1 encoding

Converting from UTF-8 to ISO-8859-1 works cleanly when the characters fit in the target encoding. If the string contains characters outside ISO-8859-1, such as Cyrillic or CJK glyphs, Ruby raises an Encoding::UndefinedConversionError unless you supply the undef: :replace option. That is why the next example covers ASCII-8BIT, which represents raw bytes and needs similar fallback handling.

Converting from ASCII-8BIT

binary_data = "hello".encode(Encoding::ASCII_8BIT)
# Convert bytes to UTF-8
binary_data.force_encoding("UTF-8")
# => "hello"

# Or use encode directly
binary_data.encode("UTF-8")
# => "hello"

ASCII-8BIT is Ruby’s encoding for binary data — strings that hold raw bytes rather than text with a known character set. Before converting such a string to UTF-8, you may need to call force_encoding to tell Ruby how to interpret the bytes. Once the encoding is set correctly, encode can transcode the content to the target encoding without data loss.

Handling invalid characters

invalid_utf8 = "hello\xFFworld"
invalid_utf8.encode("UTF-8", invalid: :replace, undef: :replace)
# => "hello?world"

The replacement character ? appears wherever a byte could not be mapped to the target encoding. This is a visible signal that the original data contained something unexpected, but it also means the conversion completes without raising an exception. For production pipelines, logging how many replacements occurred can help you detect encoding problems in the source data before they cause silent data loss.

Common Patterns

Transcoding for file I/O

content = File.read("data.csv", encoding: "UTF-8")
# Convert to ASCII for legacy systems
ascii_content = content.encode("US-ASCII", undef: :replace, invalid: :replace)
File.write("data ascii.csv", ascii_content, encoding: "US-ASCII")

Reading a file with an explicit encoding and then transcoding to a different one is common when integrating with legacy systems that expect ASCII. The conversion uses the same invalid: and undef: options to handle characters that have no direct ASCII equivalent. After conversion, the output file contains only 7-bit characters, which older tools can process without errors.

Normalizing Unicode

text = "café"
text.encode!("UTF-8")  # in-place conversion

# Use Unicode normalization forms
normalized = text.encode("UTF-8").normalize(:nfc)

Unicode normalisation is a separate step from encoding conversion. Two strings can be byte-identical in UTF-8 but represent the same character in different composed forms — for example, é as a single codepoint versus e followed by a combining accent. Calling normalize(:nfc) after encoding ensures that equivalent text compares as equal, which matters for database lookups and string comparisons.

Encoding detection and conversion

def convert_to_utf8(string)
  # First force UTF-8 encoding, then re-encode properly
  string.force_encoding("UTF-8").encode("UTF-8")
end

# Handle various input encodings
def sanitize_encoding(input)
  input.encode("UTF-8", invalid: :replace, undef: :replace)
end

When you are unsure about the incoming encoding, being explicit about the fallback behaviour keeps the conversion predictable. A single malformed byte can halt the entire process if exceptions are not caught, so specifying invalid: :replace up front is a safety net. This approach is especially helpful in web applications that accept file uploads or data feeds, where the encoding of each payload is unknown until it arrives.

Working with external APIs

# Convert string for HTTP requests
query = "search term"
encoded_query = query.encode("UTF-8")

# Convert for legacy database storage
legacy_string = "old data".encode("Windows-1252")

External APIs and databases sometimes require strings in a specific legacy encoding. Converting to UTF-8 at the application boundary and back to the required encoding at the output point keeps your internal code uniform. This pattern isolates encoding concerns to the integration layer, so the rest of the application can assume UTF-8 everywhere.

CSV encoding handling

require "csv"

# Read CSV with specific encoding
CSV.foreach("data.csv", encoding: "ISO-8859-1") do |row|
  # Convert each field to UTF-8
  utf8_row = row.map { |cell| cell.encode("UTF-8", invalid: :replace) }
  process(utf8_row)
end

CSV files from different regions often use local encodings like ISO-8859-1 or Windows-1252. The CSV.foreach method accepts an encoding: option that tells Ruby how to interpret the raw bytes before your code sees them. Converting each field individually gives you control over what happens when a specific cell contains characters that cannot be represented in UTF-8.

Email and HTTP headers

# Encode subject for email (RFC 2047)
subject = "Subject: =?UTF-8?B?#{["café"].pack("m0")}?="
# => "Subject: Y2Fmw6k="

# Decode received subject
decoded = subject.force_encoding("UTF-8")

Errors

  • Encoding::UndefinedConversionError: Raised when the conversion is not defined between encodings. Handle with invalid: and undef: options.
  • Encoding::InvalidByteSequenceError: Raised when the string contains bytes invalid for the source encoding. Use invalid: :replace to handle.
  • ArgumentError: Raised when an invalid encoding name is provided.

If you work with external text often, keep the target encoding choice close to the point where the data enters the app. That way, each parser or adapter documents its own expectation instead of relying on a hidden assumption elsewhere.

See Also