I have a strings:
AddData
TestSomething
TellMeWhoYouAre
and so on. I want to add space before uppercase letters. How can I do it?
Using sed
, and assuming you don't want a space in front of the word:
$ sed 's/\([^[:blank:]]\)\([[:upper:]]\)/\1 \2/g' file.in
Add Data
Test Something
Tell Me Who You Are
The substitution will look for an upper-case letter immediately following a another non-whitespace character, and insert a space in-between the two.
For strings with more than one consecutive upper-case character, like WeAreATeam
, this produces We Are ATeam
. To sort this, run the substitution a second time:
$ sed -e 's/\([^[:blank:]]\)\([[:upper:]]\)/\1 \2/g' \
-e 's/\([^[:blank:]]\)\([[:upper:]]\)/\1 \2/g' file.in
ReadFileFromUSBDrive
Commented
Feb 2, 2017 at 6:13
Read File From U S B Drive
or Read File From USBDrive
?
Read File From USB Drive
Commented
Feb 2, 2017 at 10:10
sed 's|\([[:upper:]]\)| &|g'
Perl, using lookbehind and lookahead zero-width regular expressions:
$ perl -pe 's/(?<=\w)(?=[A-Z])/ /g' file.in
Tell Me Who You Are ## TellMeWhoYouAre
I Am A Regular Expression User ## IAmARegulaExpressionUser
This version is also separating consecutive uppercase letters.
ReadFileFromUSBDrive
into Read File From U S B Drive
whereas the OP wanted Read File From USB Drive
.
sed -r -e "s/([^A-Z])([A-Z])/\1 \2/g"
Add space between a letter that is not an upper-case letter and a letter that is an upper-case letter
Came here to get this question answered and, after reading the other suggestions, arrived at a solution that handles the ReadFromUSBDrive
to Read From USB Drive
case, too - I adapted ka3ak's expression style and kusalananda's use of multiple expressions to get the following:
sed -r -e "s/([^A-Z])([A-Z])/\1 \2/g" -e "s/([A-Z]+)([A-Z])/\1 \2/g" file.in
e.g.
$ sed -r -e "s/([^A-Z])([A-Z])/\1 \2/g" -e "s/([A-Z]+)([A-Z])/\1 \2/g" <<EOF
AddData
TestSomething
TellMeWhoYouAre
ReadFromUSBDrive
EOF
Add Data
Test Something
Tell Me Who You Are
Read From USB Drive
Note that this will choke on single-letter words like the article "A" or the pronoun "I" when they occur directly before an abbreviation as they would be indistinguishable from the characters in an intended abbreviation, e.g. ReadAUSBDrive
will render Read AUSB Drive
.
Python solution:
#!/usr/bin/env python
from __future__ import print_function
import sys
with open(sys.argv[1]) as f:
for line in f:
for char in line:
if char.isupper():
print(" "+char,end="")
else:
print(char,end="")
Test run:
$ ./add_space_to_upper.py input.txt
Add Data
Test Something
Tell Me Who You Are
print(line[0], end="")
followed by for char in line[1:]:
to avoid printing that unwanted space at the beginning of every output line.
Commented
Feb 5, 2017 at 12:00
Using Raku (formerly known as Perl_6)
Sample Input:
AddData
TestSomething
TellMeWhoYouAre
IClimbALadder
ReadFileFromCDDrive
ReadAUSBDrive
Code and Output (1):
raku -pe 's:g/(<?after <:Ll> >) (<?before <:Lu>+ >)/ /;'
Add Data
Test Something
Tell Me Who You Are
IClimb ALadder
Read File From CDDrive
Read AUSBDrive
Code and Output (2):
raku -pe 's:g/(<?after <:L> >) (<?before <:Lu>+ >)/ /;'
Add Data
Test Something
Tell Me Who You Are
I Climb A Ladder
Read File From C D Drive
Read A U S B Drive
Above is a pretty direct translation of @JJoao's Perl5 code. The <:L>
, <:Ll>
, and <:Lu>
represent "Unicode letter", "Unicode letter-lower", and `Unicode letter-upper", respectively.
Addendum: Like others in this thread have noted, it appears that sequential s///
substitution calls can get you closer to a desired output. Below, take the results of the first Raku example (above), and run a second s///
substitution:
Code and Output (3):
raku -pe 's:g/(<?after <:Ll> >) (<?before <:Lu> >)/ /;' | perl6 -pe 's:g/(<?after <:Lu>+ >) (<?before <:Lu><:Ll>+ >)/ /;'
Add Data
Test Something
Tell Me Who You Are
I Climb A Ladder
Read File From CD Drive
Read AUSB Drive
The results with Raku (5/6 test lines separated correctly), equals the best results obtained with other languages posted thus far.
IClimbALadder
ReadFileFromCDDrive
and @Kusalananda 's solution works great.