CraZ
CraZ

Reputation: 1824

Regex: Replacing a string in a sub-string only

I have special file format where I need to replace dozens of strings and reformat its structure. As the simplest solution I have prepared my patterns file where all regex definitions/replacements are stored (~100 replacements). I'm using perl to find and replace patterns (perl -p patterns source.file). Everything so far so good.

However, there is one case I'm unable to resolve using regex. I need to replace strings in part of the whole line, i.e. replace string in within a sub-string only.

Example: For simplicity, I need to replace all "A" to "X" only in the middle string (delimited by ;).

Input line:

ABCD ABCD; ABCD ABCD; ABCD ABCD

Expected output:

ABCD ABCD; XBCD XBCD; ABCD ABCD
           ^    ^
           the only replaced characters

This correctly replaces all characters:

s/A/X/g;

But I need to replace commas in the middle field only. I tried:

s/(.*?;.*?)A/\1X/g;
s/(.*?;.*)A(.*?;)/\1X\2/g;  # alternative to find the last A

But this replaces either the first A. I can have multiple patterns like this to repeat the search&replace procedure but this does not sound like a good solution as I don't know how many A's I will have in the sub-string.

I tried to play with lookbehind but unsuccessfully. Please note, I just need a regex definition I could use in my patterns file (i.e. no perl code). Alternatively, I'm able to use sed or awk to handle this case but I'm not too much familiar with it.

Thanks, community!

Regex101: https://regex101.com/r/Ic4ciA/1

Upvotes: 1

Views: 105

Answers (2)

Toto
Toto

Reputation: 91385

A perl one liner:

echo 'ABCD ABCD; ABCD ABCD; ABCD ABCD' | perl -pe 's/(?:.+?;|\G).*?\KA(?=.*?;)/X/g'
ABCD ABCD; XBCD XBCD; ABCD ABCD

Explanation:

(?:             # non capture group
    .+?         # 1 or more any character but newline, not greedy
    ;           # semicolon
  |             # OR
    \G          # restart from last match position
)               # end group
.*?             # 0 or more any character but newline, not greedy
\K              # forget all we have seen until  this position
A               # letter A
(?=             # positive lookahead, make sure we have after:
    .*?         # 0 or more any character but newline, not greedy
    ;           # a semicolon
)               # end lookahead

Demo

Upvotes: 1

Tim Biegeleisen
Tim Biegeleisen

Reputation: 521194

I don't know of a clean way to do this in one go using a regex tool alone. But if you are open to a more iterative approach, it can fairly easily be handled in any scripting language. Here is a Python script which gets the job done:

inp = "ABCD ABCD; ABCD ABCD; ABCD ABCD"
parts = inp.split(';')

index = 1
while index < len(parts)-1:
    parts[index] = parts[index].replace('A', 'X')
    index += 1

output = ';'.join(parts)
print(output)

This prints:

ABCD ABCD; XBCD XBCD; ABCD ABCD

The approach is to split the input string on semicolon, generating a list of terms. Then, iterate from the second to second-to-last term, doing a replacement of the letter A to X. Finally, join together to produce the output you want.

Upvotes: 0

Related Questions