I'd like to indent a multiline string in Groovy but I can't figure out the right RegEx syntax / or Regex flags to achieve that.
Here's what I tried so far:
def s="""This
is
multiline
"""
println s.replaceAll('/(.*)/'," \1")
println s.replaceAll('/^/'," ")
println s.replaceAll('(?m)/^/'," \1")
println s.replaceAll('(?m)/(.*)/'," \1")
These didn't work as expected for some reason.
The only thing that worked so for is this block:
def indented = ""
s.eachLine {
indented = indented + " " + it + "\n"
}
println indented
Is there a shorter / more efficient way to indent all lines of a string in Groovy?
You need to put the (?m) directive inside the regular expression; and the pattern is a slashy string, not a single quoted string with slashes inside:
s.replaceAll(/(?m)^/, " ")
You could split and join - don't know if it's more efficient, but shorter
def s="""This
is
multiline
"""
def indent = " "
println indent + s.split("\\n").join("\n" + indent);
Or perhaps using just the replace function from java which is non-regex and potentially faster:
def s="""\
This
is
multiline
"""
println ' ' + s.replace('\n', '\n ')
which prints:
This
is
multiline
note: for those who are picky enough, replace does use the java regex implementation (as in Pattern), but a LITERAL regex which means that it will ignore all normal regex escapes etc. So the above is probably still faster than split for large strings, but this makes you wish they had left some function in there that just did a replace without any involvement of the potentially slow Pattern implementation.
Related
I had someone help with a prior question to turn hexadecimal to string, but I want the string to output surrounded with '
so it returns 'I0KB' instead of just I0KB.
What I have:
with open('D:\new 4.txt', 'w') as f:
f.write('if not (GetItemTypeId(GetSoldItem())==$49304B42) then\n')
def hex_match_to_string(m):
return ''.join([chr(int(m.group(1)[i:i+2], 16)) for i in range(0, len(m.group(1)), 2)])
# ...
line = re.sub(r'\$((?:\w\w\w\w\w\w\w\w)+)', hex_match_to_string, line)
file_out.write(line)
output:
if not (GetItemTypeId(GetSoldItem())==I0KB) then
but I want it to output
if not (GetItemTypeId(GetSoldItem())=='I0KB') then
and using
def hex_match_to_string(m):
return ''.join(',[chr(int(m.group(1)[i:i+2], 16)) for i in range(0, len(m.group(1)), 2)],')
...gives me a syntax error even though I read that join(a,b,c) is the way to combine strings.
Thanks in advance for the help, and sorry I am clueless for what should be an easy task.
You can put a backslash character followed by a quote ( \" or \' ). This is called an escape sequence and Python will remove the backslash, and put just the quote in the string.
You should not add the quotes to the argument passed to join, but wrap the result of the join with quotes:
return "'" + ''.join([chr(int(m.group(1)[i:i+2], 16)) for i in range(0, len(m.group(1)), 2)]) + "'"
I think it's important to distinguish between enclosing a string between, single, double, or triple quotation marks. See answers here regarding the most common use of the third (the so-called doc-strings).
While most of the time you can use " and ' interchangeably, you can use them together in order to escape the quotation:
>>> print("''")
''
>>> print('"')
"
You can also use the double quotes three times to escape any double quotes in between:
>>> print(""" " " "j""")
" " "j
But I'd suggest against the last option, because it doesn't always work as expected, for example, print(""""""") will throw an error. (And of course, you could always use the \ to escape any special character.)
I have a string:
Hi there, "Bananas are, by nature, evil.", Hey there.
I want to split the string with commas as the delimiter. How do I get the .split method to ignore the comma inside the quotes, so that it returns 3 strings and not 5.
You can use regex in split method
According to this answer the following regex only matches , outside of the " mark
,(?=(?:[^\"]\"[^\"]\")[^\"]$)
so try this code:
str.split(",(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*\$)".toRegex())
You can use split overload that accepts regular expressions for that:
val text = """Hi there, "Bananas are, by nature, evil.", Hey there."""
val matchCommaNotInQuotes = Regex("""\,(?=([^"]*"[^"]*")*[^"]*$)""")
println(text.split(matchCommaNotInQuotes))
Would print:
[Hi there, "Bananas are, by nature, evil.", Hey there.]
Consider reading this answer on how the regular expression works in this case.
You have to use a regular expression capable of handling quoted values. See Java: splitting a comma-separated string but ignoring commas in quotes and C#, regular expressions : how to parse comma-separated values, where some values might be quoted strings themselves containing commas
The following code shows a very simple version of such a regular expression.
fun main(args: Array<String>) {
"Hi there, \"Bananas are, by nature, evil.\", Hey there."
.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)".toRegex())
.forEach { println("> $it") }
}
outputs
> Hi there
> "Bananas are, by nature, evil."
> Hey there.
Be aware of the regex backtracking problem: https://www.regular-expressions.info/catastrophic.html. You might be better off writing a parser.
If you don't want regular expressions:
val s = "Hi there, \"Bananas are, by nature, evil.\", Hey there."
val hold = s.substringAfter("\"").substringBefore("\"")
val temp = s.split("\"")
val splitted: MutableList<String> = (temp[0] + "\"" + temp[2]).split(",").toMutableList()
splitted[1] = "\"" + hold + "\""
splitted is the List you want
I'm guessing this is a well known issue and there's an efficient workaround somehow.
I am getting output which has lines in it that contain a fixed number of empty spaces. I'm doing a string comparison test such as the one below as part of a unit test. Is there a way to get this to pass without modifying the strings using stripIndent() or the like?
Note, the test below is supposed to have 4 white spaces in the seemingly empty line between testStart and testEnd in the multiline string. However, stack overflow may be removing it?
String singleLine = 'testStart\n \ntestEnd'
String multiLine =
'''
testStart
testEnd
'''
println singleLine
println multiLine
assert singleLine == multiLine
String singleLine = 'testStart\n \ntestEnd'
String multiLine =
'''
testStart
(assume there are 4 spaces on this line)
testEnd
'''
println singleLine
println multiLine
assert singleLine == multiLine
That assertion is supposed to fail. The first character in singleLine is the character t from testStart. The first character in multiLine is a newline character because the String begins immediately after the opening ''' and the first character you have after that is a newline character. You have the same issue at the end of the string. You could solve that in a couple of ways:
String multiLine =
'''\
testStart
(assume there are 4 spaces on this line)
testEnd\
'''
Or:
String multiLine =
'''testStart
(assume there are 4 spaces on this line)
testEnd'''
This was being caused by an intelliJ default setting. I have now resolved it.
http://blog.darrenscott.com/2015/01/24/intellij-idea-14-how-to-stop-stripping-of-trailing-spaces/
I am getting really confused in Groovy. When to use what for Strings in Groovy ?
1) Single Quotes - ' '
2) Double Quotes - " "
3) Triple Quotes - '''
My code:
println("Tilak Rox")
println('Tilak Rox')
println('''Tilak Rox''')
All tend to produce same results.
When to Use What ?
I would confuse you even more, saying, that you can also use slash /, dolar-slash $/ and triple-double quotes """ with same result. =)
So, what's the difference:
Single vs Double quote: Most important difference. Single-quoted is ordinary Java-like string. Double-quoted is a GString, and it allows string-interpolation. I.e. you can have expressions embedded in it: println("${40 + 5}") prints 45, while println('${ 40 + 5}') will produce ${ 40 + 5}. This expression can be pretty complex, can reference variables or call methods.
Triple quote and triple double-quote is the way to make string multiline. You can open it on one line in your code, copy-paste big piece of xml, poem or sql expression in it and don't bother yourself with string concatenation.
Slashy / and dollar-slashy $/ strings are here to help with regular expressions. They have special escape rules for '\' and '/' respectfully.
As #tim pointed, there is a good official documentation for that, explaining small differences in escaping rules and containing examples as well.
Most probably you don't need to use multiline/slashy strings very often, as you use them in a very particular scenarios. But when you do they make a huge difference in readability of your code!
Single quotes ' are for basic Strings
Double quotes " are for templated Strings ie:
def a = 'tim'
assert "Hi $a" == 'Hi tim'
Triple single quotes ''' are for multi-line basic Strings
Triple double """ quotes are for multi-line templated strings
There's also slashy strings /hello $a/ which are templated
And dollar slashy Strings $/hello $a/$ which are multi-line and templated
They're all documented quite well in the documentation
I'm embedding JRuby in Java, because I need to call some Ruby methods with Java strings as arguments. The thing is, I'm calling the methods like this:
String text = ""; // this can span over multiple lines, and will contain ruby code
Ruby ruby = Ruby.newInstance();
RubyRuntimeAdapter adapter = JavaEmbedUtils.newRuntimeAdapter();
String rubyCode = "require \"myscript\"\n" +
"str = build_string(%q~"+text+"~)\n"+
"str";
IRubyObject object = adapter.eval(ruby, codeFormat);
The thing is, I don't know what strings I can use as delimiters, because if the ruby code I'm sending to build_string will contain ruby code. Right know I'm using ~,but I think this could break my code. What characters can I use as delimiters to make sure my code will work no matter what the ruby code is?
use the heredoc format:
"require \"myscript\"\n" +
"str = build_string(<<'THISSHOUDLNTBE'\n" + text + "\nTHISSHOULDNTBE\n)\n"+
"str";
this however assumes you won't have "THISSHOULDNTBE" on a separate line in the input.
Since string text contain contain any character, there is no character left to use for quotation escaping like the ~ you're using now. You would still need to escape the tilde in string text in java and append that one to the string you're building.
Something like (untested, not a Java guru):
String rubyCode = "require \"myscript\"\n" +
"str = build_string(%q~" + text.replaceAll("~", "\\~") + "~)\n"+
"str";