In a fortran code for example, there are lines like
c write(6,*) "some text"
write(6,*) "some other text"
How would I search for lines having a write
but exclude all lines starting with c
or C.
Vi and Vim Stack Exchange is a question and answer site for people using the vi and Vim families of text editors. It only takes a minute to sign up.
Sign up to join this communityYou can use the regular expression:
^\([^cC].*\)\?write
Or, using "very magic" to simplify a little:
\v^([^cC].*)?write
\v
: turns on "very magic" so we don't have to use as many escape characters,^
: matches a start of the line,([^cC].*)?
: matches either nothing, or any character that is not a c/C followed by zero or more characters:
[^cC]
: matches any character that is not a c/C — this is the part that ignores lines that start with c/C,.*
: matches a string of any characters of any length (including zero-length) — this part matches the rest of the line preceding the "write",(...)?
: makes the entire expression inside the parentheses optional — this part means that lines that start with "write" will still be matched,write
: matches "write"!UPDATE: As @KarlYngveLervåg points out in the comments, the instances of write
that you want to search for will be (almost) always be the first thing on the line, so you could probably get away with using a much simpler regular expression:
^\s*write
^
: matches the start of the line,\s*
: if any whitespace follows, this matches it,write
: matches "write".UPDATE 2: If you're actually carrying out a :substitute
operation, you can use another method, which is to use :vglobal
to only perform the :substitute
command on lines which don't match a pattern:
:v/^c/s/write/changed
:v/^c/
On all lines that don't match the regular expression ^c
i.e. all lines that don't begin with a "c",s/write/changed
Replace "write" with "changed".The same technique also works with :global
, to perform replacements only on lines that do match a regular expression.
^\s*write
, since it is the first thing on the line.
Jan 24, 2017 at 15:54
write
somewhere in the middle.
write
is a statement in Fortran, and so it may only be preceded by a comment letter or whitespace. Perhaps also, very rarely, a number label, but I don't think so. Thus it would make sense to simplify the regexes accordingly. Of course, your answer is perfectly correct, but I think it is worth mentioning this simplification.
Jan 24, 2017 at 19:12
x=1; y=2; write(*,*)x,y
Nevertheless, I agree with you that the simplification is worth mentioning. I'll update my answer.
If you're going to use this pattern for substitution, you could also use zero-width lookarounds, in particular \@<!
:
/\@<!
\@<! Matches with zero width if the preceding atom does NOT match just
before what follows. Thus this matches if there is no position in the
current or previous line where the atom matches such that it ends just
before what follows. /zero-width {not in Vi}
Like "(?<!pattern)" in Perl, but Vim allows non-fixed-width patterns.
The match with the preceding atom is made to end just before the match
with what follows, thus an atom that ends in ".*" will work.
Warning: This can be slow (because many positions need to be checked
for a match). Use a limit if you can, see below.
Example matches
\(foo\)\@<!bar any "bar" that's not in "foobar"
\(\/\/.*\)\@<!in "in" which is not after "//"
For example:
\(^[cC].*\)\@<!write