rubyguides

String#chomp

chomp removes trailing line separators from input text. It handles the common line-ending styles (Unix LF, old Mac CR, Windows CRLF) and lets you specify custom separators. That makes it a small but dependable tool for cleaning up content from files, terminals, and network input.

Signature

str.chomp(separator=$/) → new_string

Parameters:

  • separator — the string to remove from the end. Defaults to $/ (the input record separator, typically "\n").

Returns: A new string with the trailing separator removed, or the original string if no separator matches.

Default behavior

With no argument, chomp removes "\n", "\r", or "\r\n" from the end:

"hello\n".chomp
# => "hello"

"hello\r\n".chomp
# => "hello"

"hello\r".chomp
# => "hello"

Each of these three examples demonstrates a different line-ending style that chomp recognises by default. Unix systems use \n, old Mac OS used \r, and Windows uses the two-character sequence \r\n. The method identifies which style is present and removes it in a single call, so you do not need to inspect the input beforehand.

If the string doesn’t end with the separator, chomp returns a copy unchanged:

"hello".chomp
# => "hello"

That “no match” case matters because chomp is careful to leave the rest of the input alone. You can call it freely without worrying that it will strip spaces or other content that is not part of the line ending.

Custom separators

Pass a string to remove a specific trailing sequence:

"hello\n\n".chomp("\n")
# => "hello\n"

"data.txt\r\n".chomp("\r\n")
# => "data.txt"

"hello!!!".chomp("!!!")
# => "hello"

When you pass a custom separator, chomp removes exactly that substring if it appears at the very end of the input. The separator is treated as literal text, not a pattern, so chomp("!") removes a single exclamation mark rather than all trailing exclamation marks. This makes the method predictable when you know the exact terminator your data uses.

Multiple occurrences are removed only from the end:

"hello!!!".chomp("!")
# => "hello!!"

"hello!!!".chomp("!!!")
# => "hello"

This behaviour is useful when the trailing marker has meaning but repeated trailing markers should be trimmed away. It keeps the method precise — chomp("!") only removes one ! from "hello!!!", whereas chomp("!!!") removes all three. Understanding this distinction helps you choose the right separator length for the data format you are cleaning.

The bang variant

chomp! modifies the receiver in place:

s = "hello\n"
s.chomp!
# => "hello"
s
# => "hello"

# Returns nil if nothing to chomp:
s2 = "hello"
s2.chomp!
# => nil
s2
# => "hello"

Use the bang version when you want to update the same string object in place. It saves a reassignment step, but it also means later code sees the modified value.

Practical Examples

Reading lines from a file

The most common use is cleaning up lines read from a file or stdin:

File.readlines("config.txt").each do |line|
  # Without chomp, line includes the trailing "\n"
  puts line.chomp
end

This is probably the most common chomp pattern because file input often includes a trailing newline on every line. Removing the newline up front keeps text comparisons and formatting code cleaner, and it prevents an extra blank line from appearing in the output when you print each line with puts.

Parsing multi-line input

input = "alice,bob,charlie\n"

name, rest = input.chomp.split(",", 2)
name
# => "alice"
rest
# => "bob,charlie"

Chomping before the split gives you the part you want without carrying the trailing newline into the array. That is a simple way to keep line-oriented input easy to slice into fields.

Avoiding nil when there’s no newline

Unlike strip, chomp never returns nil when given no argument — it always returns a string:

"no newline here".chomp
# => "no newline here" (a copy, not nil)

That guarantee is one of the reasons chomp is safer than methods that may return nil. You can keep chaining string methods without having to guard every call with a nil check. The behaviour is different from the bang variant chomp!, which does return nil when nothing was removed.

Special separator values

Empty string

An empty string separator removes no characters:

"hello\n".chomp("")
# => "hello\n" (unchanged)

Nil

Passing nil as the separator tells chomp to remove nothing at all — it returns a copy of the receiver unchanged. This is another no-op case alongside the empty separator, and it exists so that separator values coming from variables do not cause unexpected behaviour when they happen to be nil. The method always returns a fresh copy, never nil, which makes it safe to chain with other text operations.

Passing nil removes nothing — returns a copy of the string:

"hello\n".chomp(nil)
# => "hello\n" (unchanged)

Custom multi-character separator

Passing nil or an empty string as the separator makes chomp a no-op — it returns a copy of the original string without removing any characters. This is useful when the separator value comes from a configuration or a variable that might be empty, because the method degrades gracefully instead of raising an error. The same graceful handling applies to nil: the method simply returns the string unchanged, which keeps conditional logic simple.

record = "field1,field2,field3---"
record.chomp("---")
# => "field1,field2,field3"

Multi-character separators are handy for record formats that use a custom terminator instead of a newline. The method still only trims from the end, so the middle of the string stays untouched.

Common Pitfalls

CRLF on different systems

If your input has Windows line endings ("\r\n") but you’re running on Unix, chomp handles them automatically:

# On Unix, reading a file with CRLF:
content = File.read("windows_file.txt")
content.chomp
# removes the \n, leaving \r at the end
# => "line with trailing \r"

For reliable cross-platform handling, normalise the line endings before calling chomp. The gsub call replaces both \r\n (Windows) and standalone \r (old Mac) with \n, producing consistent Unix-style line endings. After that normalisation pass, chomp only needs to handle a single \n at the end of the string.

content.gsub(/\r\n?/, "\n").chomp

Normalising line endings first makes downstream processing simpler when your input comes from mixed platforms. chomp then removes only the final line break, which keeps the behaviour predictable. For files with a mix of \n and \r\n, a single gsub pass before chomp is often the cleanest approach.

chomp vs strip

chomp only removes line endings. strip removes all whitespace from both ends:

"  hello  \n".chomp
# => "  hello  " (spaces preserved)

"  hello  \n".strip
# => "hello" (spaces removed too)

The difference is important when spaces are meaningful data rather than formatting noise. chomp is the narrower tool, so it is the safer default when you only want to remove line endings.

chomp! returns nil on no match

Unlike some bang methods, chomp! returns nil (not the string) when no separator is present:

result = "no newline".chomp!
# result is nil, not "no newline"
# This matters in conditional expressions

That return value makes the bang method useful in conditionals, but it also means you need to check for nil if you expect no change. The non-bang version is simpler when you only need a cleaned-up copy.

See Also