4

I have a source text file containing text where some words are l e t t e r s p a c e d like the word "letterspaced" in this question (i.e., there is a space character between the letters of the word.

How can I undo letterspacing using sed?

A pattern like \{[A-Za-z] \}+[A-Za-z] captures a letterspaced word, and s/ //g takes the spaces out, but how do I extract a letterspaced word out of a line of text and undo letterspacing without harming the legitimate space characters in the rest of the text?

5

You can do it like this:

sed     -e's/ \([^ ][^ ]\)/\n\1/g' \
        -e's/\([^ ][^ ]\) /\1\n/g' \
        -e's/ //g;y/\n/ /
'       <<\IN
I have a source text file containing text where
some words are l e t t e r s p a c e d
like the word "letterspaced" in this question
(i.e., there is a space character between the
letters of the word. 
IN

The idea is to first find all spaces which are either preceded by or followed by two or more not-space characters and set them aside as newline characters. Next simply remove all remaining spaces. And last, translate all newlines back to spaces.

This is not perfect - without incorporating an entire dictionary of every word you could possibly use the best you will get is some kind of heuristic. This one's pretty good, though.

Also, depending on the sed you use, you might have to use a literal newline in place of the n I use in the first two substitution statements as well.

Aside from that caveat, though, this will work - and work very fast - with any POSIX sed. It doesn't need to do any costly lookaheads or behinds, because it just saves impossibles, which means it can handle all of pattern space for each substitution in a single address.

OUTPUT

I have a source text file containing text where some
words are letterspaced
like the word "letterspaced" in this question
(i.e., there is a space character between the
letters of the word.
3

A Perl approach that mostly works:

perl -C -lpe 's/(?:^|\P{L})\K\p{L}(?:\s\p{L})+(?=\P{L}|$)/$&=~s{\s}{}rgo/goe'

This assumes a version of Perl recent enough to know about the /r flag in replacements.

Proof of concept:

$ echo  'Do I like «ł é t t ê r s p ä c è đ» text?' | perl -C -lpe 's/(?:^|\P{L})\K\p{L}(?:\s\p{L})+(?=\P{L}|$)/$&=~s{\s}{}rgo/goe'
Do I like «łéttêrspäcèđ» text?
  • @don_crissti Yes, that's one reason why I said it mostly works. It won't merge letterspaced words separated by two or more spaces though. – lcd047 Jul 3 '15 at 16:21
0

Perl's lookahead assertions make this simple. AFAIK, sed lacks these.

Given that two or more whitespaces separate the words, this eliminates single spaces but leaves unaltered sequences of two or more:

perl -pe 's/\s(?!\s)//g' myfile

The p switch causes Perl to read myfile and then substitute single spaces (\s) which are NOT followed by another space. This is the negative lookahead assertion given by (?!\s).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.