Skip to content

Ruby Strings

A String object in Ruby holds and manipulates a sequence of characters. Under the hood, these characters are represented by bytes according to the string’s encoding (typically UTF-8 in modern Ruby).

String literals can be created using single quotes (') or double quotes (").

Single-quoted strings perform minimal processing. Most escape sequences (like \n) are not interpreted, and expression interpolation is not supported.

'This is a simple Ruby string literal. \n is not a newline here.'

If you need to place an apostrophe within a single-quoted string, precede it with a backslash:

'Won\'t you read O\'Reilly\'s book?'

Double-quoted strings support various escape sequences and expression interpolation.

"This string supports interpolation: #{1 + 1} and escape sequences like newlines \n and tabs \t."

The backslash also escapes another backslash in both types of quotes, so \\ becomes a single backslash character.

You can also create strings using String.new:

empty_string = String.new
greeting = String.new("Hello")

Consider using # frozen_string_literal: true at the top of your Ruby files. This pragma makes all string literals in the file frozen by default, which can improve performance and prevent accidental modifications.

# frozen_string_literal: true
my_string = "hello"
# my_string << " world" # This would raise a FrozenError

Expression substitution allows embedding the value of any Ruby expression into a double-quoted string (or other interpolating contexts like %Q{}) using #{ and }:

#!/usr/bin/env ruby
# frozen_string_literal: true
x, y, z = 12, 36, 72
puts "The value of x is #{x}."
puts "The sum of x and y is #{x + y}."
puts "The average was #{(x + y + z) / 3}."

This will produce the following result:

The value of x is 12.
The sum of x and y is 48.
The average was 40.

Ruby provides percent string syntax (%) for creating strings with various behaviors, using arbitrary delimiters. Common forms include:

  • %q{...} or %q(...) or %q[...], etc.: Non-interpolated string (like single quotes). Delimiters can be any matching pair of non-alphanumeric characters or balanced pairs like (), [], {}, <>.
  • %Q{...} or %{...}: Interpolated string (like double quotes).
  • %x{...}: Interpolated string that is executed as a shell command, returning its standard output (like backticks “).
  • %w{word1 word2}: Non-interpolated array of words, split by whitespace.
  • %W{word1 #{expr} word2}: Interpolated array of words, split by whitespace.
# Equivalent to "Ruby is fun."
str1 = %Q{Ruby is fun.}
# Equivalent to 'Ruby is fun.'
str2 = %q[Ruby is fun.]
# Equivalent to `ls -l` (executes command)
# output = %x(ls -l)
# Example with different delimiters
puts %q(Don't need to escape apostrophes here.)
puts %Q[Interpolation works: #{2 * 3}]

The following table lists common escape sequences. These are interpreted in double-quoted strings and similar contexts (e.g., %Q{}). In single-quoted strings, only \' and \\ have special meaning.

NotationDescription
\Backslash
’Single quote
”Double quote
\aBell or alert (0x07)
\bBackspace (0x08)
\eEscape (0x1b)
\fFormfeed (0x0c)
\nNewline (0x0a)
\rCarriage return (0x0d)
\sSpace (0x20)
\tTab (0x09)
\vVertical tab (0x0b)
\nnnOctal notation (n is 0-7)
\xnnHexadecimal notation (n is 0-9, a-f, A-F)
\unnnnUnicode character (nnnn is hex)
\u{nnnn …}Unicode character(s) (one or more hex codes)

Modern Ruby (1.9 and later) has robust support for character encodings. Strings are encoding-aware, and UTF-8 is the most commonly used and recommended encoding for Ruby applications.

Ruby manages several default encoding settings:

  • Encoding.default_external: Specifies the default encoding for strings created from external sources (e.g., files, network sockets). It’s often set based on your system’s locale (e.g., LANG environment variable, typically UTF-8 on modern systems).
  • Encoding.default_internal: If set, strings read from external sources are automatically transcoded from default_external to this default_internal encoding. It’s often left as nil unless specific internal processing requires a different encoding.
  • Script Encoding: The encoding of your Ruby source file itself. You should specify this using a ‘magic comment’ at the top of your file, especially if your source code contains non-ASCII characters in string literals or comments:
# encoding: utf-8
# Your Ruby code here...
# Or, more commonly combined with frozen string literal pragma:
# frozen_string_literal: true
# encoding: utf-8
MY_CONSTANT = "你好世界" # Hello world in Chinese

Without this magic comment, Ruby might assume a different encoding (like US-ASCII if no UTF-8 BOM is present), potentially misinterpreting string literals. UTF-8 is the universally recommended script encoding.

You can inspect a string’s encoding using my_string.encoding. To convert a string to another encoding, use my_string.encode('Target-Encoding') or my_string.encode(Encoding::TARGET_ENCODING). If you know a string’s byte sequence represents a different encoding than Ruby assumed, you can use my_string.force_encoding('Correct-Encoding').

#!/usr/bin/env ruby
# encoding: utf-8
s = "hello résumé"
puts "Original string: '#{s}', Encoding: #{s.encoding}"
begin
s_latin1 = s.encode(Encoding::ISO_8859_1)
puts "Encoded to Latin1: '#{s_latin1}', Encoding: #{s_latin1.encoding}"
rescue Encoding::UndefinedConversionError => e
puts "Error encoding to Latin1: #{e.message}"
# This can happen if s contains characters not representable in ISO-8859-1
end
# Example of force_encoding (use with caution)
byte_sequence = "\xC3\xA9".b # A UTF-8 sequence for 'é', .b makes it ASCII-8BIT
puts "Byte sequence: #{byte_sequence.inspect}, Encoding: #{byte_sequence.encoding}"
utf8_char = byte_sequence.force_encoding(Encoding::UTF_8)
puts "Forced to UTF-8: '#{utf8_char}', Encoding: #{utf8_char.encoding}"
# The `$KCODE` variable used in older Ruby versions (pre-1.9) is deprecated and has no effect.

Strings in Ruby are objects of the String class, which provides a rich set of methods for manipulation. To call a String method, you typically use an instance of a String object:

my_string = "Initial Value"
# or
# my_string = String.new("Initial Value")

Example:

#!/usr/bin/env ruby
# frozen_string_literal: true
my_str = String.new("THIS IS A TEST")
foo = my_str.downcase
puts "Lowercase string: #{foo}"

This will produce the following result:

Lowercase string: this is a test

Below are some common public String methods (assuming str is a String object). For a complete list, refer to the official Ruby documentation for the String class.

IDMethod and Description
1str % arg
Formats a string using a format specification (similar to sprintf). arg can be a single value or an array for multiple substitutions. Example: "Hello %s" % "World". String interpolation (#{}) or Kernel#sprintf are often preferred for readability.
2str * integer
Returns a new string containing str repeated integer times.
3str + other_str
Concatenates other_str to str, returning a new string.
4str << obj
Concatenates obj to str, modifying str in place. If obj is an Integer between 0-255, it’s converted to a character. Returns str.
5str <=> other_str
Compares str with other_str, returning -1 (less than), 0 (equal), or 1 (greater than). Comparison is case-sensitive and based on byte values.
6str == obj
Tests for equality. If obj is not a String, returns false. Otherwise, returns true if str <=> obj is 0.
7str =~ regexp
Matches str against the regular expression regexp. Returns the starting index of the match, or nil if no match. Sets global variables like $& (matched string). Consider str.match?(regexp) for a boolean result or str.match(regexp) for a MatchData object without setting global variables as much.
8str.capitalize
Returns a new string with the first character converted to uppercase and the rest to lowercase.
9str.capitalize!
Same as capitalize, but modifies str in place. Returns str or nil if no changes were made.
10str.casecmp(other_str)
Case-insensitive comparison of strings. Returns -1, 0, or 1. str.casecmp?(other_str) (Ruby 2.4+) returns true or false.
11str.center(width, padstr=’ ‘)
Returns a new string of length width, with str centered and padded with padstr.
12str.chomp(separator=$/)
Returns a new string with the given separator removed from the end of str (if present). $/ is the input record separator, typically a newline. If separator is "", it removes all trailing newlines.
13str.chomp!(separator=$/)
Same as chomp, but modifies str in place. Returns str or nil.
14str.chop
Returns a new string with the last character removed. If the string ends with \r\n, both are removed.
15str.chop!
Same as chop, but modifies str in place. Returns str or nil.
16str.concat(other_str)
Alias for str << other_str.
17str.count(char_set_1, …, char_set_n)
Counts characters in str. With one argument, counts occurrences of characters in char_set_1. With multiple arguments, counts characters present in the intersection of all char_sets. Character sets can use ^ for negation and - for ranges (e.g., "a-z").
18str.crypt(salt_str)
WARNING: This method uses a weak, often DES-based, hashing algorithm based on the system’s crypt(3). It is NOT suitable for secure password storage in modern applications. Consider using libraries like bcrypt-ruby for robust password hashing.
19str.delete(char_set_1, …, char_set_n)
Returns a copy of str with all characters in the intersection of its arguments deleted.
20str.delete!(char_set_1, …, char_set_n)
Same as delete, but modifies str in place. Returns str or nil.
21str.downcase
Returns a copy of str with all uppercase letters replaced with lowercase. Supports Unicode case mapping if available.
22str.downcase!
Same as downcase, but modifies str in place. Returns str or nil.
23str.dump
Returns a version of str with all non-printing characters replaced by \nnn notation and special characters escaped. Suitable for debugging or string literals.
24str.each_char { |char| block }
Passes each character of str to the block. Returns an Enumerator if no block is given.
25str.each_byte { |byte| block }
Passes each byte (as an integer 0-255) from str to the block. Returns an Enumerator if no block is given.
26str.each_line(separator=$/,chomp: false) { |line| block }
Splits str using separator, passing each substring (line) to the block. The chomp option (Ruby 2.6+) automatically chomps lines. Returns an Enumerator if no block is given.
27str.empty?
Returns true if str has a length of zero.
28str.eql?(other)
Two strings are eql? if they have the same length and content. Used by Hash for key equality.
29str.gsub(pattern, replacement) or str.gsub(pattern) { |match| block }
Returns a copy of str with all occurrences of pattern (String or Regexp) replaced with replacement or the block’s result.
30str.gsub!(pattern, replacement) or str.gsub!(pattern) { |match| block }
Same as gsub, but modifies str in place. Returns str or nil.
31str[index] or str[start, length] or str[range] or str[regexp] or str[regexp, capture_group_index] or str[match_string]
Element reference (slicing). Accesses characters or substrings. Negative indices count from the end.
32str[index] = new_val or str[start, length] = new_val … (similar to accessors)
Element assignment. Replaces parts of the string.
33str.hash
Returns an integer hash value for str. Used by Hash objects.
34str.hex
Treats leading characters of str as hexadecimal digits (optional 0x prefix and sign) and returns the corresponding integer. Returns 0 on error.
35str.include?(substring)
Returns true if str contains the given substring.
36str.index(substring_or_regexp [, offset])
Returns the index of the first occurrence of substring_or_regexp in str, starting search from offset if given. Returns nil if not found.
37str.insert(index, other_str)
Inserts other_str into str before the character at index. Modifies str.
38str.inspect
Returns a printable version of str, with special characters escaped (similar to dump but often used for puts and p).
39str.intern or str.to_sym
Returns the Symbol corresponding to str, creating it if it doesn’t exist.
40str.length
Returns the length of str in characters (respecting encoding). Alias: size.
41str.ljust(width, padstr=’ ‘)
Returns a new string of length width, with str left-justified and padded with padstr.
42str.lstrip
Returns a copy of str with leading whitespace removed.
43str.lstrip!
Removes leading whitespace from str in place. Returns str or nil.
44str.match(pattern [, pos])
Converts pattern to a Regexp (if not already) and matches it against str starting at pos. Returns a MatchData object or nil.
45str.match?(pattern [, pos])
Similar to match, but returns true or false and doesn’t update $~ or create a MatchData object, making it faster for boolean checks (Ruby 2.4+).
46str.oct
Treats leading characters of str as octal digits (optional sign) and returns the corresponding integer. Returns 0 on error.
47str.replace(other_str)
Replaces the content of str with other_str. Modifies str in place.
48str.reverse
Returns a new string with the characters of str in reverse order.
49str.reverse!
Reverses str in place. Returns str.
50str.rindex(substring_or_regexp [, offset])
Returns the index of the last occurrence of substring_or_regexp in str, searching backward from offset if given. Returns nil if not found.
51str.rjust(width, padstr=’ ‘)
Returns a new string of length width, with str right-justified and padded with padstr.
52str.rstrip
Returns a copy of str with trailing whitespace removed.
53str.rstrip!
Removes trailing whitespace from str in place. Returns str or nil.
54str.scan(pattern) or str.scan(pattern) { |match_capture_array| block }
Iterates through str, matching pattern (Regexp or String). For each match, results are added to an array or passed to the block. If pattern has captures, an array of captures is yielded/returned for each match.
55str.slice(…) / str.slice!(…)
Aliases for str[...] and str[...] = ... (destructive slice). slice! deletes the specified portion and returns it.
56str.split(pattern=$;, limit=0)
Divides str into substrings based on pattern (String or Regexp delimiter), returning an array. $; is the default field separator (nil by default, meaning split on whitespace). limit controls the number of fields.
57str.squeeze([other_char_sets]*)
Returns a new string where runs of consecutive identical characters (optionally restricted to those in other_char_sets) are replaced by a single character.
58str.squeeze!([other_char_sets]*)
Same as squeeze, but modifies str in place. Returns str or nil.
59str.strip
Returns a copy of str with leading and trailing whitespace removed.
60str.strip!
Removes leading and trailing whitespace from str in place. Returns str or nil.
61str.sub(pattern, replacement) or str.sub(pattern) { |match| block }
Returns a copy of str with the first occurrence of pattern replaced.
62str.sub!(pattern, replacement) or str.sub!(pattern) { |match| block }
Same as sub, but modifies str in place. Returns str or nil.
63str.succ or str.next
Returns the successor to str (e.g., “a”.succ -> “b”, “9”.succ -> “10”, “az”.succ -> “ba”).
64str.succ! or str.next!
Modifies str to its successor in place. Returns str.
65str.swapcase
Returns a copy of str with uppercase alphabetic characters converted to lowercase and vice-versa. Supports Unicode if available.
66str.swapcase!
Same as swapcase, but modifies str in place. Returns str or nil.
67str.to_f
Returns the result of interpreting leading characters in str as a floating-point number. Returns 0.0 on error.
68str.to_i(base=10)
Returns the result of interpreting leading characters in str as an integer in the given base (2-36). Returns 0 on error.
69str.to_s or str.to_str
Returns self (the string itself).
70str.tr(from_str, to_str)
Returns a copy of str with characters in from_str replaced by corresponding characters in to_str. Supports ^ negation and c1-c2 ranges.
71str.tr!(from_str, to_str)
Same as tr, but modifies str in place. Returns str or nil.
72str.tr_s(from_str, to_str)
Processes like tr, then squeezes runs of translated characters.
73str.tr_s!(from_str, to_str)
Same as tr_s, but modifies str in place. Returns str or nil.
74str.unpack(format)
Decodes str (which may contain binary data) according to format string, returning an array of extracted values. See unpack directives below.
75str.upcase
Returns a copy of str with all lowercase letters replaced with uppercase. Supports Unicode case mapping if available.
76str.upcase!
Same as upcase, but modifies str in place. Returns str or nil.
77str.upto(other_str, exclusive_end=false) { |s| block }
Iterates from str to other_str (inclusive by default) using succ, passing each value to the block. Returns an Enumerator if no block given.

The String#unpack method uses a format string consisting of single-character directives to decode binary data. Each directive can be followed by a number (repeat count) or * (consume rest). Refer to Ruby documentation for full details as directives can be complex and platform-dependent in some cases (e.g. native sizes).

DirectiveReturnsDescription
AStringArbitrary binary string (remove trailing nulls and spaces).
aStringArbitrary binary string.
BStringBit string (MSB first).
bStringBit string (LSB first).
CInteger8-bit unsigned integer (unsigned char).
cInteger8-bit signed integer (signed char).
D, dFloatDouble-precision float, native format.
EFloatDouble-precision float, little-endian byte order.
eFloatSingle-precision float, little-endian byte order.
F, fFloatSingle-precision float, native format.
GFloatDouble-precision float, network (big-endian) byte order.
gFloatSingle-precision float, network (big-endian) byte order.
HStringHex string (MSB first nibble).
hStringHex string (LSB first nibble).
IIntegerUnsigned integer, native byte order (size can be specified with _ or !). I_ or I! implies native int size.
iIntegerSigned integer, native byte order. i_ or i! implies native int size.
LIntegerUnsigned 32-bit integer, native byte order (L_, L!). L> big-endian, L< little-endian.
lIntegerSigned 32-bit integer, native byte order (l_, l!). l> big-endian, l< little-endian.
MStringQuoted-printable encoded string.
mStringBase64 encoded string.
NIntegerUnsigned 32-bit integer, network (big-endian) byte order.
nIntegerUnsigned 16-bit integer, network (big-endian) byte order.
PStringPointer to a structure (fixed-length string).
pStringPointer to a null-terminated string.
QIntegerUnsigned 64-bit integer, native byte order (Q_, Q!). Q> big-endian, Q< little-endian.
qIntegerSigned 64-bit integer, native byte order (q_, q!). q> big-endian, q< little-endian.
SIntegerUnsigned 16-bit integer, native byte order (S_, S!). S> big-endian, S< little-endian.
sIntegerSigned 16-bit integer, native byte order (s_, s!). s> big-endian, s< little-endian.
UIntegerUTF-8 character as an integer codepoint.
uStringUU-encoded string.
VIntegerUnsigned 32-bit integer, VAX (little-endian) byte order.
vIntegerUnsigned 16-bit integer, VAX (little-endian) byte order.
wIntegerBER-compressed integer.
XN/ASkip backward one byte.
xN/ASkip forward one byte (null byte).
ZStringNull-terminated string (remove trailing nulls). With *, reads until first null.
@N/ASkip to the offset given by the length argument.

The unpack method is powerful for parsing binary data formats.

#!/usr/bin/env ruby
# encoding: ascii-8bit # Often used for binary data
puts "abc \0\0abc \0\0".unpack('A6Z6').inspect # => ["abc", "abc "]
puts "abc \0\0".unpack('a3a3').inspect # => ["abc", " \x00\x00"]
puts "abc \0abc \0".unpack('Z*Z*').inspect # => ["abc ", "abc "]
puts "aa".unpack('b8B8').inspect # => ["10000110", "01100001"] (depends on 'a' ASCII value)
puts "aaa".unpack('H2H2c').inspect # => ["61", "61", 97] (ASCII 'a' is 0x61, decimal 97)
puts "\xFE\xFF\xFE\xFF".unpack('sS').inspect # => [-2, 65534] (assuming native short is 16-bit little-endian for this example)
puts "now=20is".unpack('M*').inspect # => ["now is"]
puts "whole".unpack('x a x2 a X2 a X a X2 a').inspect # => ["h", "e", "l", "l", "o"] (demonstrating skips)
# Example with network byte order integers
data = [1024, 20480].pack('Nn') # Pack two integers into a binary string
puts data.unpack('Nn').inspect # => [1024, 20480]

For more detailed examples and explanations of pack and unpack directives, consult the official Ruby documentation for Array#pack and String#unpack.