You would have to do this in a loop:
s/\(^.*title="[^" ]*\) \([^"]*".*$\)/\1_\2/
s/\(title="[^" ]*\) \([^"]*"\)/\1_\2/
or (probably again faster, because why add another match just to replace it by itself?!)
s/\(title="[^" ]*\) /\1_/
and use sed's test-and-branch feature, retrying the substitute until no more changes are made. The point of the patterns in this command is to split the line at the first (remaining) space and replace that space with an underscore.
Here is a script:
sed -e ':loop' \
-e 's/\(title="[^" ]*\) \([^"]*"\)/\1_\2/' \
-e 't loop' <foo.in >foo.out
diff -u foo.in foo.out
The initial answer used a wider pattern, but @g-man commented that it was not necessary. It was slower, as illustrated by timing sed on a 10Mb file (tested with GNU sed on Debian 7):
27.03user 0.01system 0:27.18elapsed 99%CPU (0avgtext+0avgdata 1104maxresident)k
0inputs+0outputs (0major+333minor)pagefaults 0swaps
9.54user 0.00system 0:09.60elapsed 99%CPU (0avgtext+0avgdata 972maxresident)k
0inputs+0outputs (0major+301minor)pagefaults 0swaps
With OSX, the difference is not as much:
Interestingly enough, the wider pattern does not work with Solaris's sed (but the shorter one does). It does not match either end of the line within the grouping
\), while both BSD and GNU do this. Likewise, it works with HPUX 11.31 and AIX 7.1
POSIX sed uses BREs, and the grouping feature is covered by 9.3.6 BREs Matching Multiple Characters:
A subexpression can be defined within a BRE by enclosing it between the character pairs
"\)". Such a subexpression shall match whatever it would have matched without the "(" and ")", except that anchoring within subexpressions is optional behavior; see BRE Expression Anchoring. Subexpressions can be arbitrarily nested.
9.3.8 BRE Expression Anchoring explains the term:
A BRE can be limited to matching strings that begin or end a line; this is called "anchoring".
so, in the context of the standard as it is implemented, this is a known limitation of Solaris
sed which the standard allows as "optional" behavior.