0

I have the following use case. I need a Regex pattern to only match a line, if part of the string does not contain a different string. Here is an example:

<androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"/>

So here I want to match android:layout_marginStart="12dp" so that I can replace with:

android:layout_marginStart="12dp"
android:layout_marginLeft="12dp"

I have worked this one out and I can use the following regex to do it:

Find: (.*)android:layout_marginStart="(.*)" Replace: $1android:layout_marginStart="$2"\n$1android:layout_marginLeft="$2"

What I can't do is conditionally match. I do not want to match if this xml object already contains the android:layout_marginLeft attribute.

StuStirling
  • 15,601
  • 23
  • 93
  • 150
  • 2
    [Regex is probably not the tool for parsing XML](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags). – ggorlen Mar 26 '19 at 16:41
  • Possible duplicate of [RegEx match open tags except XHTML self-contained tags](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags) – Yassin Hajaj Mar 26 '19 at 16:42
  • Whilst I understand there is another post asking about checking XML tags, there is also an answer on there https://stackoverflow.com/a/1733489/507313 that also says using regex for a task like this is fine if its a one time thing which this is. – StuStirling Mar 26 '19 at 16:49

1 Answers1

1

In regex, if you want to check to make sure a string is not coming up after the part you wish to match, you can use a negative lookahead.

In this example, you want to match something, but only if the string layout_marginLeft is not coming up later. You can do that, but throwing layout_marginLeft into a negative lookahead, like this:

(?:(?!layout_marginLeft).)*

Now, when you combine that with what you actually want to match your regex would look something like this:

(android:layout_marginStart="(.*?)")(?:(?!layout_marginLeft).)*(?=/>)

And then your replacement string would look like this:

\1\n\t\tandroid:layout_marginLeft="\2"

So, the replacement stuff works the same way, it's just that you are telling it not to do a replacement on anything that already contains layout_marginLeft.

Here is a demo

Quixrick
  • 3,190
  • 1
  • 14
  • 17
  • Thank you for you answer. Very useful. Is there a way to check both after and before? So not to match: `android:layout_marginLeft="10dp" android:layout_marginRight="10dp"` – StuStirling Mar 26 '19 at 17:00
  • [Here you go, brother](https://regex101.com/r/09YpwA/2). Notice that I added the single-line `s` flag to scan across multiple lines. This also meant that I had to change the `.` in `"(.*?)"` to not include spaces. `"([^\s]*?)"`. Finally, I added the same negative lookahead to the front of the expression. So now it is in two places. – Quixrick Mar 26 '19 at 17:15
  • Massive thank you for taking the time to fix and explain it. I would never have reached this on my own. – StuStirling Mar 27 '19 at 11:13
  • Sorry, not done yet. When using in Java I get an issue with the `\K` character as it's not supported. Not sure how to resolve. People mention about using capturing groups but can't get these to work. – StuStirling Mar 27 '19 at 11:34
  • are you able to help with the last piece of the puzzle? – StuStirling Mar 29 '19 at 09:43