String
String.new, String() The String class is fundamental to Ruby, representing sequences of characters. Ruby’s strings are versatile, mutable, and natively support UTF-8 encoding.
Creating Strings
# Literals
"hello" # => "hello"
'hello' # => "hello" (no interpolation)
%q{hello} # => "hello"
%Q{hello #{world}} # => "hello world"
# Constructor
String.new("hello") # => "hello"
String(42) # => "42"
Ruby offers several literal forms, each with different interpolation and escaping rules. Double-quoted strings support interpolation with #{} while single-quoted and %q{} forms treat everything literally. The String() conversion method is a safe way to coerce objects to strings, returning nil for objects that don’t define a string conversion.
String Methods
Querying
"hello".length # => 5
"hello".size # => 5
"hello".empty? # => false
"".empty? # => true
"hello".bytesize # => 5 (UTF-8 bytes)
These query methods answer common questions about a string’s state without changing it. length and size are aliases for character count, while bytesize reports the byte count, which differs when multi-byte characters are present. These are non-destructive checks you can call freely in conditionals and guards.
Modification
# Concatenation
"hello" + " world" # => "hello world"
"hello" << " world" # => "hello world" (mutating)
"hello" * 3 # => "hellohellohello"
The + operator creates a new string each time, while << modifies the receiver in place. The difference matters when building strings in a loop, where repeated + calls allocate intermediate objects that increase memory pressure. The << operator or the * repetition operator are more efficient for repeated concatenation.
Case Conversion
"HELLO".downcase # => "hello"
"hello".upcase # => "HELLO"
"Hello".capitalize # => "Hello"
"HeLLo".swapcase # => "hEllO"
Ruby provides both non-mutating and mutating (bang) versions of case methods. The standard versions return new strings while downcase! and friends modify the receiver and return nil if no change was made. Use the bang variants only when you control the original string and want to avoid an extra allocation.
Searching and Replacing
"hello".include?("ll") # => true
"hello".index("ll") # => 2
"hello".start_with?("he") # => true
"hello".end_with?("lo") # => true
"hello".gsub("l", "r") # => "herro"
Search methods like include? and index return boolean and position results respectively, making them useful in conditional expressions and loops. gsub performs global substitution and returns a new string, while gsub! modifies in place. The question-mark convention signals predicate methods that return true or false.
Encoding
# Default UTF-8
"hello".encoding # => #<Encoding:UTF-8>
# Convert encoding
"hello".encode("ASCII") # => "hello"
"héllo".encode("ASCII", invalid: :replace) # => "?ll?"
Ruby strings default to UTF-8, which handles the full Unicode range natively. When you need to work with other encodings, encode transcodes the bytes and force_encoding relabels without converting. The invalid: :replace option substitutes a placeholder for characters that cannot be represented in the target encoding.
Practical Examples
Splitting and Joining
"a,b,c".split(",") # => ["a", "b", "c"]
["a", "b", "c"].join("-") # => "a-b-c"
split breaks a string into an array at each occurrence of the delimiter, while join does the reverse. Together they form a round-trip pair for text transformation pipelines. When the delimiter is a regular expression, split can handle multi-character or pattern-based separators.
Stripping Whitespace
" hello ".strip # => "hello"
"hello\n".chomp # => "hello"
strip removes whitespace from both ends, which makes it excellent for user input cleanup. chomp is more targeted, removing only trailing newlines from line-oriented data. These small cleanup methods prevent downstream logic from having to handle leading or trailing whitespace characters.
Interpolation
name = "World"
"Hello, #{name}!" # => "Hello, World!"
String interpolation embeds Ruby expressions directly in double-quoted strings using #{}. Any expression is evaluated and its to_s result is inserted, making it cleaner than manual concatenation. This is the preferred way to build strings from variables in Ruby.
Multiline Strings
# Heredoc
text = <<~TEXT
This is a multiline
string in Ruby
TEXT
# % notation
text = <<-TEXT
Indented heredoc
TEXT
The squiggly heredoc <<~ strips leading whitespace automatically, keeping indentation clean in source code. The older <<- form preserves indentation as-is. For embedded DSLs, SQL queries, or template strings, heredocs keep the text readable without escaping quote characters.
Mutable Strings
Unlike some languages, Ruby strings are mutable:
str = "hello"
str << " world" # Mutates: "hello world"
str.upcase! # Mutates: "HELLO"
Mutability means the same string object can change over time, which affects how you reason about state. Methods ending with ! signal in-place mutation, while their non-bang counterparts return new strings and leave the original unchanged. Being explicit about which you use makes the code easier to follow.
String behavior to remember
Strings often look simple, but they carry a few important rules in Ruby.
original = "hello"
mutated = original << " world" # original is now "hello world"
They can be mutated in place, they can be created with different literal forms, and they may need explicit encoding handling when text comes from outside the program. That means string code should be clear about whether it is building a new value or changing an existing one. When you keep those choices visible, later cleanup, formatting, and comparison steps become much easier to follow.
It also helps to remember that string operations often chain together. A value might be stripped, split, encoded, and interpolated in a single flow, so each step should make the next one easier to read. When the text is user-facing, clear names and small helper methods do more for maintainability than clever one-liners. That keeps the code close to the problem it is solving instead of forcing the reader to reconstruct the path afterward.
Encoding is another place where being explicit pays off. Text that comes from files, APIs, or terminals may not be in the format you expect, so a quick check or conversion step can save a lot of confusion later. Once the text is normalized, the rest of the string methods become much more predictable.
The same care applies when strings are built from mixed sources. A value that starts in one encoding and ends up in another can be hard to diagnose if the conversion is hidden. Making that step visible in the code helps the text pipeline stay understandable throughout the pipeline.
String encoding issues
# Force encoding
str = "hello".force_encoding("UTF-8")
# Valid encoding check
"hello".valid_encoding? # => true
The String class provides extensive functionality for text processing, making Ruby excellent for tasks involving string manipulation.