In IRB on Ruby 1.8.7, I have a collection of strings I'm working with that have newlines in them. When these newlines are output, I want to explicitly see the \r and \n characters within my strings. Is there some way to tell puts to escape those characters, or a method similar to puts that will do what I want?
Note that directly evaluating each string isn't satisfactory because I want to be able to do something like this:
=> mystrings.each { |str| puts str.magical_method_to_escape_special_chars }
This is\na string in mystrings.
This is another\n\rstring.
And don't want to have to do this:
=> mystrings[0]
"This is\na string in mystrings."
=> mystrings[1]
"This is another\n\rstring."
...
=> mystrings[1000]
"There are a lot of\n\nstrings!"
I can use the string#dump method:
=> mystrings.each { |str| puts str.dump }
This is\na string in mystrings.
This is another\n\rstring.
According to the Ruby documention for String, string#dump
Produces a version of str with all nonprinting characters replaced by
\nnn notation and all special characters escaped.
1.8.7 :001 > s = "hi\nthere"
=> "hi\nthere"
1.8.7 :002 > p s
"hi\nthere"
Related
AKA How do I find an unescaped character sequence with regex?
Given an environment set up with:
#secret = "OH NO!"
$secret = "OH NO!"
##secret = "OH NO!"
and given string read in from a file that looks like this:
some_str = '"\"#{:NOT&&:very}\" bad. \u262E\n##secret \\#$secret \\\\###secret"'
I want to evaluate this as a Ruby string, but without interpolation. Thus, the result should be:
puts safe_eval(some_str)
#=> "#{:NOT&&:very}" bad. ☮
#=> ##secret #$secret \###secret
By contrast, the eval-only solution produces
puts eval(some_str)
#=> "very" bad. ☮
#=> OH NO! #$secret \OH NO!
At first I tried:
def safe_eval(str)
eval str.gsub(/#(?=[{#$])/,'\\#')
end
but this fails in the malicious middle case above, producing:
#=> "#{:NOT&&:very}" bad. ☮
#=> ##secret \OH NO! \###secret
You can do this via regex by ensuring that there are an even number of backslashes before the character you want to escape:
def safe_eval(str)
eval str.gsub( /([^\\](?:\\\\)*)#(?=[{#$])/, '\1\#' )
end
…which says:
Find a character that is not a backslash [^\\]
followed by two backslashes (?:\\\\)
repeated zero or more times *
followed by a literal # character
and ensure that after that you can see either a {, #, or $ character.
and replace that with
the non-backslash-maybe-followed-by-even-number-of-backslashes
and then a backslash and then a #
How about not using eval at all? As per this comment in chat, all that's necessary are escaping quotes, newlines, and unicode characters. Here's my solution:
ESCAPE_TABLE = {
/\\n/ => "\n",
/\\"/ => "\"",
}
def expand_escapes(str)
str = str.dup
ESCAPE_TABLE.each {|k, v| str.gsub!(k, v)}
#Deal with Unicode
str.gsub!(/\\u([0-9A-Z]{4})/) {|m| [m[2..5].hex].pack("U") }
str
end
When called on your string the result is (in your variable environment):
"\"\"\#{:NOT&&:very}\" bad. ☮\n\##secret \\\#$secret \\\\\###secret\""
Although I would have preferred not to have to treat unicode specially, it is the only way to do it without eval.
I have a form where I put in hashes with regular expression values. My problem is that they gets messed up when travelling from my view, through my controller and into MongoDB with Mongoid. How do I preserve the regex'es?
Input examples:
{:regex1 => "^Something \(#\d*\)$"}
{:regex2 => "\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z"}
My formtastic view form looks like this:
= semantic_form_for resource, :html => {:class => "form-vertical"} do |r|
= r.inputs do
= r.input :value, :as => :text
= r.actions do
= r.action :submit
My controller create action takes in the params and handles it like this:
class EmailTypesController < InheritedResources::Base
def create
puts params[:email_type][:value] # => {:regex1 => "^Something \(#\d*\)$"} and
# {:regex2 => "\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z"}
puts params[:email_type][:value].inspect # => "{:regex1 => \"^Something \\(#\\d*\\)$\"}" and
# "{:regex2 => \"\\A[\\w+\\-.]+#[a-z\\d\\-.]+\\.[a-z]+\\z\"}"
params[:email_type][:value] = convert_to_hash(params[:email_type][:value])
puts params[:email_type][:value] # => {"regex1"=>"^Something (#d*)$"} and
# {"regex2"=>"A[w+-.]+#[a-zd-.]+.[a-z]+z"}
create! do |success, failure|
success.html {
redirect_to resource
}
failure.html {
render :action => :new
}
end
end
def convert_to_hash(string)
if string.match(/(.*?)=>(.*)\n*/)
string = eval(string)
else
string = string_to_hash(string)
end
end
def string_to_hash(string)
values = string.split("\r\n")
output = {}
values.each do |v|
val = v.split("=")
output[val[0].to_sym] = val[1]
end
output
end
end
Firing up the console and inspecting the values put in through Mongoid:
Loading development environment (Rails 3.2.12)
1.9.3p385 :001 > EmailType.all.each do |email_type|
1.9.3p385 :002 > puts email_type.value
1.9.3p385 :003?> end
{"regex1"=>"^Something (#d*)$"}
{"regex2"=>"A[w+-.]+#[a-zd-.]+.[a-z]+z"}
=> true
1.9.3p385 :004 >
The problem lies in ruby's evaluation of strings, which ignores useless escapes:
puts "^Something \(#\d*\)$".inspect
=>"^Something (#d*)$"
That is to say the eval simply ignores the backslash. Note that typically in ruby regexes aren't created using strings but through their own regex literal, so that
/^Something \(#\d*\)$/.inspect
=>"/^Something \\(#\\d*\\)$/"
Notice the double backslash instead of single. This means that eval has to receive two backslashes instead of one in the string, as it has to be eval'd into a single backslash character.
A quick and easy way to do this is to simply run a sub ob the string before the convert_to_hash call:
# A little confusing due to escapes, but single backslashes are replaced with double.
# The second parameter is confusing, but it appears that String#sub requires a few
# extra escapes due to backslashes also being used to backreference.
# i.e. \n is replaced with the nth regex group, so to replace something with the string
# "\n" yet another escape for backslash is required, so "\\n" is replaced with "\n".
# Therefore the input of 8 blackslashes is eval'd to a string of 4 backslashes, which
# sub interprets as 2 backslashes.
params[:email_type][:value].gsub!('\\', '\\\\\\\\')
this shouldn't be a problem unless you are using backslashes in the hash keys at some point, in which case more advanced matching would be needed to extract only the regex's and perform the substitution on them.
I would like to keep \n when I print out strings in ruby,
Like now, if I use puts or print, \n will end up with a newline:
pry(main)> print "abc\nabc"
abc
abc
is there a way to let ruby print it out like: abc\nabc ?
UPDATE
Sorry that maybe I didn't make it more clear. I am debugging my regexps, so when I output a string, if a \n is displayed as a \n, not a newline, it would be easier for me to check. So #slivu 's answer is exactly what I want. Thanks guys.
i would suggest to use p instead of puts/print:
p "abc\nabc"
=> "abc\nabc"
and in fact with pry you do not need to use any of them, just type your string and enter:
str = "abc\nabc"
=> "abc\nabc"
str
=> "abc\nabc"
In Ruby, strings in a single quote literal, like 'abc\nabc' will not be interpolated.
1.9.3p194 :001 > print 'abc\nabc'
abc\nabc => nil
vs with double quotes
1.9.3p194 :002 > print "abc\nabc"
abc
abc => nil
Because \n is a special keyword, you have to escape it, by adding a backslash before the special keyword (and because of that, backslashes must also be escaped), like so: abs\\nabc. If you wanted to print \\n, you would have to replace it to be abs\\\\n, and so on (two backslashes to display a backslash).
You can also just use single quotes instead of double quotes so that special keywords will not be interpreted. This, IMHO, is bad practice, but if it makes your code look nicer, I guess it's worth it :)
Here are some examples of ways you can escape (sort of like a TL;DR version):
puts 'abc\nabc' # Single quotes ignore special keywords
puts "abc\\nabc" # Escaping a special keyword (preferred technique, IMHO)
p "abc\nabc" # The "p" command does not interpret special keywords
You can also do it like this
puts 'abc\nabc'
Use %q to create a string for example:
str = %q{abc\nklm}
puts str #=> 'abc\nklm'
or
puts %q{abc\nklm}
or use escape character
puts "abc\\nklm"
To print the string: abc\nabc
The coded string must be: abc\\nabc which will not generate a newline.
Is there any single-line string literal syntax in Ruby that allows string interpolation but does not interpret a backslash as an escape character?
I.e.,
Where ruby_var = "foo"
I want to be able to type the equivalent of C:\some\windows\path\#{ruby_var}\path resulting in the string C:\some\windows\path\foo\path without having to escape the backslashes or resort to a multi-line heredoc.
puts "C:\some\windows\path\#{ruby_var}\path"
puts "C:\some\windows\path\path_#{ruby_var}\path"
=> C: omewindowspath#{ruby_var}path
=> C: omewindowspathpath_foopath
puts 'C:\some\windows\path\#{ruby_var}\path'
puts 'C:\some\windows\path\path_#{ruby_var}\path'
=> C:\some\windows\path\#{ruby_var}\path
=> C:\some\windows\path\path_#{ruby_var}\path
puts %{C:\some\windows\path\#{ruby_var}\path}
puts %{C:\some\windows\path\path_#{ruby_var}\path}
=> C: omewindowspath#{ruby_var}path
=> C: omewindowspathpath_foopath
puts %q{C:\some\windows\path\#{ruby_var}\path}
puts %q{C:\some\windows\path\path_#{ruby_var}\path}
=> C:\some\windows\path\#{ruby_var}\path
=> C:\some\windows\path\path_#{ruby_var}\path
ruby_var = "hello"
puts 'C:\some\windows\path\%s\path' % ruby_var
#=>C:\some\windows\path\hello\path
'C:\some\windows\path\%s\path' % ruby_var
#=> 'C:\some\windows\path\foo\path'
I don't think it is possible.
You should consider using forward slashes instead to make it look prettier; I believe the standard ruby libraries in Windows won't care what kind of slashes you use.
There is also:
File.join('C:', 'path', ruby_var)
In Ruby 1.8.6 (2007-09-24 patchlevel 111):
str = '\&123'
puts "abc".gsub("b", str) => ab123c
puts "abc".gsub("b", "#{str}") => ab123c
puts "abc".gsub("b", str.to_s) => ab123c
puts "abc".gsub("b", '\&123') => ab123c
puts "abc".gsub("b", "\&123") => a&123c <--- This I want to achieve using temporary variable
If I change str = '\&123' to str = "\&123" it works fine, but I get str from match function, so I cannot specify it manually within parentheses. Is there any way to change the 'string' to "string" behavior?
maybe there is a simpler way, however the code below works
> str = '\&123'
> puts "abc".gsub("b", str.gsub(/\\&/o, '\\\\\&\2\1'))
> => a\&123c
Simple:
str = '\&123' <-- the result of your match function
str = str.gsub(/\\/, '\\\\')
You may also want to take a look here.
#Valentin
-> I meant that str from match was not taken verbatim. Thus another (simpler) solution appeared, that I was not aware of....
"abc".gsub("b") { str } -> a\&123c
Just remove the backslash:
puts "abc".gsub("b", '&123')
There is no need to protect the ampersand with a backslash inside
single-quoted string literals (unlike double-quoted ones).