0

I'm facing a herculean task in multiline find and replace (explained in separate parts).

The solution I seek involves using regex in Notepad++ with either the built in Find & Replace or with the multiline search and replace dialog made available by the NPPtoolbucket plugin.

This is an example extract of the input file:

ALPHA('Hello John')
IGNORE111
IGNORE222
BETA('Hi Mary') 

I need to replace Hi Mary (fourth line) with Hello John (content fetched from first line). i.e, the actual content in the fourth line between BETA(' and ') should be replaced by the content fetched between ALPHA(' and ') from first line.

The desired outcome should be:

ALPHA('Hello John')
IGNORE111
IGNORE222
BETA('Hello John')

The issue I'm facing is I have 47 IFC files with (12000+ lines each). These files have an assembly name in the first line (unique to each IFC file) and it needs to be used to replace content somewhere in lines 48, 87 and many more lines in all of the 47 IFC files. It all follows a particular pattern. Can you suggest any trick to achieve this using regex?

Say like - find using (ALPHA\(')(.*)(')(NEW_SYNTAX_I_SEEKING)(BETA\(')(.*)(')

and replace with \1\2\3\4\5\2\7

where

(ALPHA\(') will become back ref \1

(.*) will become back ref \2, it will split as -> Hello John

(') will become back ref \3

(NEW_SYNTAX_I'M_SEEKING) will become back ref \4; this will be the new regex syntax I'm looking for, which will fetch content spread across multiple lines and the one I dont want any change

(BETA\(') will become back ref \5

(.*) will become back ref \6, it will split as -> Hi Mary, so we can use back ref \2 to replace back ref \6.

(') will become back ref \7

Hope I got my question & intention articulated properly. I'll highly appreciate any help given.

Cheers, JJ

  • What've you tried? What you could achieve? – Máté Juhász Mar 10 '17 at 11:09
  • I would use a scripting language to read the text to be copied first and perform the substitution in a second command. Actually, this could allow you to run the script once cycling all 47 files. – simlev Mar 10 '17 at 11:25
  • From your description it seems that only the first line contains the string needed for the substitutions, and the subsitutions are to be performed on the subsequent lines of the same file. If it's that simple, you can manually open each file, copy the string and write a regex search & replace. – simlev Mar 10 '17 at 11:28
  • @Máté Juhász I m stuck at (ALPHA(')(. *)(') . After that I cannot find any command to split from first character at second line till ' after BETA( at fourth line – user705628 Mar 10 '17 at 11:32
  • @simlev yes I can open all forty seven files and do it individually. I was wondering if I can use the replace in all opened documents feature. So it gets over in few seconds – user705628 Mar 10 '17 at 11:34
0

If you work with text files a lot you will love awk.

awk -i inplace 'NR==1 && match($0, /.*\('\''(.+)'\''\)/,matches) {name = matches[1]; print $0} /IGNORE/ {print $0} NR>1 && !/IGNORE/ {print gensub (/([\w ]*\('\'').+('\''\))/, "\\1"name"\\2", "1")}' *.txt

The explanation is going to take a while, let me first break down the command into three sections, each made up of a condition and a command:

  • NR==1 && match($0, /.*\('\''(.+)'\''\)/,matches) {name = matches[1]; print $0} this prints the first line and copies what in your case is the assembly name to a variable simply called name.
  • /IGNORE/ {print $0} if lines match the text IGNORE, just print them.
  • NR>1 && !/IGNORE/ {print gensub (/([\w ]*\('\'').+('\''\))/, "\\1"name"\\2", "1")} perform regex replace on the remaining lines, employing the name variable created earlier.

A few more details follow:

awk this is a tool for manipulating text files, alternatively I would recommend perl.

-i inplace this means the original files are going to be edited (make backups!). Disclaimer: I couldn't test this setting yet because it requires a more recent awk version than the one I have installed.

' the command is a string, therefore it is encapsuled in apostrophes.

NR==1 this is a condition, the line number must be 1.

&& this means AND.

match( this is another condition that must be met: a regexp match function that takes 3 arguments.

$0 first argument: this represents the whole line.

/.*\('\''(.+)'\''\)/ second argument, the regular expression

matches third argument, the variable where the matching strings are to be stored.

{ here start the actions that are to be carried out in case the conditions are true.

name = matches[1] the variable name is created and it is assigned to be equal to the first capture group (same as backreference \1).

; the semicolon separates instructions.

print $0 we print the first line, too.

/IGNORE/ look for lines that contain the text IGNORE.

{print $0} just print them.

NR>1 && !/IGNORE/ condition: for all lines except the first one, if they don't contain the text IGNORE.

{print print the result of the replace. gensub ( function that performs search and replace allowing the use of backreferences.

/([\w ]*\('\'').+('\''\))/ the search pattern. Here the sequence '\'' is what is needed in order to insert a single '.

"\\1"name"\\2" the replace pattern. "\1" and "\2" are two backreferences.

"1" means that only the first match is to be replaced.

' end of the awk command.

*.txt run awk on all files with extension .txt in the current directory.

Note: I know that you are asking how to do this in Notepad++, but I believe you should consider command line tools. The reason is that graphical programs are more apt to perform a one-time operation, but in the comment you specify you would like to automate the work and process 47 files at once. Command line is more apt to automation than graphical interfaces, that's my point.

To get started, you have gawk (GNU awk) for Windows and if you want to proceed further you can work on Linux or install a Linux-like environment like Cygwin.

  • Thanks a lot for the detailed explanation & intro to awk. I ll definitely explore it as I work with lot of text files. In between, I got a simple solution for my question, Just a matter of switching on an option. Explained in the answer part. – user705628 Mar 11 '17 at 10:59
  • Great, you should accept your own answer then. – simlev Mar 11 '17 at 18:24
  • I want to clear few doubts before accepting – user705628 Mar 13 '17 at 10:53
  • One is like after back reference 9, i have an issue say for back ref 10. If I give \10 it takes back ref \1 and concatenate "0". for \12 it takes value of \1 and concatenate "2". Is there a limit for back ref? – user705628 Mar 13 '17 at 10:57
  • Ok solved .. it is better to use $ instead of \ – user705628 Mar 13 '17 at 11:07
0

The following answer edited - As it is advisable to use $ instead of \ when we have more than 9 back references

A bit of tweaking with information gathered from forums helped me.

The solution is pretty simple ( The trick is in step 2)

1) Open built in Find & Replace in notepad++

2) make sure you check the:

[ ] . matches newline option

3) In find what, type

( ALPHA \ ( ' ) ( . * ) ( ' \ ) ) ( . * ) ( BETA \ ( ' ) ( .* ) ( ') )

Note - the spaces are given above for clarity. There no spaces at all in between and should be removed

4) In replace with, type

$1$2$3$4$5$$2$7

5) click "replace" / "replace all" / "replace all in all open documents" as per your needs.

Bingo!

Explanation

( ALPHA \ (' ) will become back ref $1

(.*) will become back ref $2, it will split as -> Hello John

(' \ )) will become back ref $3

(.*) will become back ref $4; This which will fetch content spread across multiple lines and the one I dont want any change

(BETA \ (') will become back ref $5

(.*) will become back ref $6, it will split as -> Hi Mary, so we can use back ref $2 to replace back ref $6.

(' \ )) will become back ref $7

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.