How to use regex to indent code in notepad++ - regex

for example I have the following code
Module MPI
Use MPI
!
! MPI info
If (true) Then
Print *, ''
! empty line 1
! empty line 2
End If
Integer ithread, nthread, ierr
End Module MPI
The lines start with ! sign which is comment lines in fortran. I want those comment lines have the same indent as their previous line indent.
That is I want this format
Module MPI
Use MPI
!
! MPI info
If (true) Then
Print *, ''
! empty line 1
! empty line 2
End If
Integer ithread, nthread, ierr
End Module MPI
I want to do this in notepad++ using regex. But if there are better choice feel free to answer.
Here is what I have tried: replace ^(\s*)(.*?\r\n)\s*\! as $1$2$1!. However it produce
Module MPI
Use MPI
!
! MPI info
If (true) Then
Print *, ''
! empty line 1
! empty line 2
End If
Integer ithread, nthread, ierr
End Module MPI
There is still two lines not right. It seems that though the pattern ^(\s*)(.*?\r\n)\s*\! matches this line, however, it just skip it for the regex engine already matched previous lines.
My question is how to solve this indent problem with regex?

Using the search text ^( +)(.*\R)(!) and the replace text \1\2\1\3 then clicking on "Replace all" twice does what is wanted on the sample text. I cannot see a way of doing this in one pass.
The expression looks for a line with leading spaces followed by a line starting with a !. The capture groups are the leading spaces in \1, the rest of that line including the newline in \2 and the leading ! in \3. The replacement just assembles the captures in the right order. Note that you could omit the capture group around the ! and just have an explicit `! in the replacement, but I like to use captures in such contexts as they often allow for shorter replacements (although not in this case) and easier enhancements.

Since the engine is already passed on a comment line to indent it, I think it is impossible to use the same entire edited line for the next match to get the number of spaces. So I think you have to repeat the same replacement more times. Try with:
^(\s*)([^!\s].*?\r\n(\1\!.*?\r\n)*)\s*\!
always replacing it with $1$2$1!.
Like I said in the comment, if you have at most N consecutive comment lines, you will click on the "replace all" button N times

Related

How do I print a Fortran string with quotes around it?

Suppose I have a Fortran program like the following:
character*30 changed_string1
changed_string1="hello"
write(*,"(A)")changed_string1(1:3)
end
I would like to print the string with quotes so that I can exactly see leading and trailing spaces. How to do this?
There is no edit descriptor for characters which outputs them along with delimiters. A character variable does not have "automatic" delimiters like those which appear in a literal character constant (although may have them as content).
Which means you have to explicitly print any chosen delimiter yourself, adding them to the format or concatenating as in Vladimir F's answer.
Similarly, you can also add the delimiters to the output list (with
corresponding format change):
write (*,'(3A)') '"', string, '"'
You can even write a function which returns a "delimited string" and use the
result in the output list:
implicit none
character(50) :: string="hello"
print '(A)', delimit(string,'"')
contains
pure function delimit(str, delim) result(delimited)
character(*), intent(in) :: str, delim
character(len(str)+2*len(delim)) delimited
delimited = delim//str//delim
end function delimit
end program
The function result above could even be deferred length (character(:), allocatable :: delimited) to avoid the explicit statement of result length.
As yamajun reminds us in a comment, a connection for formatted output has a delimiter mode, which does allow quotes and apostrophes to be added automatically to the output for list-directed and namelist output (only). For example, we can control the delimiter mode for a particular data transfer statement:
write(*, *, delim='quote') string
write(*, *, delim='apostrophe') string
or for the connection as a whole:
open(unit=output_unit, delim='quote') ! output_unit from module iso_fortan_env
Don't forget that list-directed output will add that leading blank to your output, and if you have quotes or apostrophes in your character output item you will not see exactly the same representation (this could even be what you want):
use, intrinsic :: iso_fortran_env, only : output_unit
open(output_unit, delim='apostrophe')
print*, "Don't be surprised by this output"
end
Fortran 2018 doesn't allow arbitrary delimiter choice in this way, but this could still be suitable for some uses.
You can print quotes around your string. That will enable see the leading and trailing spaces.
write(*,"('''',A,'''')") changed_string1
or with the same effect
write(*,"(3A)") "'",changed_string1,"'"
(also mentioned by francescalus) that print a ' character before and afgter your string,
or you can concatenate your string with these characters and print the result
write(*,"(A)") "'"//changed_string1//"'"

How to read large amount of data from files and store them in variable?

I have a file which has values of pressure at every element. I require the element number (ELNO) and pressure (PLOAD) and pass through a subroutine. I am not able to read them separately from the file.
The data would read like given below starting with S175..
I want to be able to read this file, say A0001.txt, and read the lines one-by-one. When reading the lines I want to store the number after the first dot, for eg 1007, 1008 etc into a variable ELNO and the number after the last comma in a variable PLOAD in a loop. This is because I will require each value of ELNO and check a condition with the IF loop.
My problem is reading the file and storing in a variable like ELNO(i) something like that. The name S175 is constant.
I think I understand the logic. I have to store each like as a string and start iterating from the 6th position in the string till it finds the first "," and store that in ELNO(i). But I am new to FORTRAN and not able to get it. I have been trying for the past week learning FORTRAN to do this. But, not able to do this problem.
I tried a code like this below but its not reading line by line since I did not put it under a loop I guess.
S175.1007,P,0.221948
S175.1008,P,0.221943
S175.1009,P,0.221929
S175.1010,P,0.222287
S175.1018,P,0.222438
S175.1019,P,0.222425
.....
.....
.....
.....
.....
S175.13000,P,-1990
S175.13001,P,-1980
S175.13002,P,-2009
PROGRAM BARGE
implicit none
CHARACTER X*80
OPEN(UNIT=60, FILE="A0001.txt", ACCESS='SEQUENTIAL', ACTION='READ')
READ(UNIT=60, FMT='(A)', END=10)X
10 OPEN(UNIT=61, FILE="2.txt", ACTION="write")
WRITE (UNIT=61,FMT='(A)')X
END PROGRAM BARGE
Thank you everyone. I have completed it myself. There might be an easier and faster method but this works fine. Let me know if there is a more efficient method. I could learn. :)
PROGRAM BARGE
implicit none
CHARACTER PRES*80
INTEGER :: SUCCESS
INTEGER :: K, L, M, ELNO ! K is for the element number,L is word P and M is for pressure value
REAL :: PLOAD
OPEN(UNIT=60, FILE="1.txt", ACCESS='SEQUENTIAL', ACTION='READ')
DO
READ(UNIT=60, FMT='(A)', IOSTAT=SUCCESS)PRES
IF (SUCCESS.NE.0) EXIT
K=6
DO WHILE (PRES(K:K) .NE. ',')
K=K+1
END DO
READ(PRES(6:K-1), *) ELNO
PRINT *, ELNO ! ELEMENT NUMBER
L=K+1
DO WHILE (PRES(L:L) .NE. ',')
L=L+1
END DO
M=L+1
DO WHILE (PRES(M:M) .NE. ' ')
M=M+1
END DO
READ(PRES(L+1:M-1), *) PLOAD ! PRESSURE ON THE ELEMENT
PRINT *, PLOAD
OPEN(UNIT=61, FILE="2.txt", ACTION="write")
WRITE (UNIT=61,FMT='(A)')PRES
END DO
READ (*,*) PRES
END PROGRAM BARGE
It would be easy to use Fortran's list-directed input to read the data items from a line such as S175.1019,P,0.222425. The following snippet can replace the do loop in OP's own answer:
DO
READ(UNIT=60, FMT='(A)', IOSTAT=SUCCESS) PRES
IF (SUCCESS.NE.0) EXIT
READ(PRES(6:),*) ELNO, P, PLOAD
WRITE(*,*) ELNO, PLOAD
END DO
For this to work you have to include a declaration such as
CHARACTER(LEN=1) :: P
to catch the letter P in each line of the input file. The important line is this one
READ(PRES(6:),*) ELNO, P, PLOAD
which uses the edit descriptor * which tells the compiler / run-time to figure out how to read values for the three variables (one integer, one character, one real) from the 6th and following characters in the line PRES.
Fortunately, with a nice clean input file such as the one shown the compiler has no trouble reading the values, all the scanning for occurrences of , is unnecessary. If, for another application, you ever need to search a line for occurrences of a character use the intrinsic function index.

How do i delete first 2 lines which match with a text given by me ( using sed )?

How do i delete first 2 lines which match with a text given by me ( using sed ! )
E.g :
#file.txt contains following lines :
abc
def
def
abc
abc
def
And i want to delete first 2 "abc"
Using "sed"
While #EdMorton has pointed out that sed is not the best tool for this job (if you wonder why exactly, see my answer below and compare it to the awk code), my research showed that the solution to the generalized problem
Delete occurences "N" through "M" of a line matching a given pattern using sed
indeed is a very tricky one in my opinion. There seem to be many suggestions for how to replace the "N"th occurence of a matching pattern with sed, but I found that deleting a specific matching line (or a range of lines) is a much more complex undertaking.
While the generalized problem with arbitrary values for N, M, and the pattern would probably be solved best by writing a "sed script generator" on the basis of a Finite State Machine, the solution to the special case asked by the OP is still simple enough to be coded by hand. I must admit that I wasn't very familiar with the obfuscated intricacies of the sed command syntax before, but I found this challenge to be quite useful for gaining more experience with non-trivial sed usage.
Anyway, here's my solution for deleting the first two occurences of a line containing "abc" in a file. If there's a simpler approach, I'm eager to learn about it, as this has taken me some time now.
A final caveat: this assumes GNU sed, as I was unable to find a solution with POSIX sed:
sed -n ':1;/abc/{n;b2;};p;$b4;n;b1;:2;/abc/{n;b3;};p;$b4;n;b2;:3;p;$b4;n;b3;:4;q' file
or, in more verbose syntax:
sed -n '
# BEGIN - look for first match
:first;
/abc/ {
# First match found. Skip line and jump to second section
n; bsecond;
};
# Line does not match. Print it and quit if end-of-file reached
p; $bend;
# Advance to next line and start over
n; bfirst;
# END - look for first match
# BEGIN - look for second match
:second;
/abc/ {
# Second match found. Skip line and jump to final section
n; bfinal;
}
# Line does not match. Print it and quit if end-of-file reached
p; $bend;
# Advance to next line and start over
n; bsecond;
# END - look for second match
# BEGIN - both matches found; print remaining lines
:final;
# Print line and quit if end-of-file reached
p; $bend;
# Advance to next line and start over
n; bfinal;
# END - print remaining lines
# QUIT
:end;
q;
' file
sed is for simple substitutions on individual lines, that is all. For anything else you should be using awk:
$ awk '!(/abc/ && ++c<3)' file
def
def
abc
def

Remove the first character of each line and append using Vim

I have a data file as follows.
1,14.23,1.71,2.43,15.6,127,2.8,3.06,.28,2.29,5.64,1.04,3.92,1065
1,13.2,1.78,2.14,11.2,100,2.65,2.76,.26,1.28,4.38,1.05,3.4,1050
1,13.16,2.36,2.67,18.6,101,2.8,3.24,.3,2.81,5.68,1.03,3.17,1185
1,14.37,1.95,2.5,16.8,113,3.85,3.49,.24,2.18,7.8,.86,3.45,1480
1,13.24,2.59,2.87,21,118,2.8,2.69,.39,1.82,4.32,1.04,2.93,735
Using vim, I want to reomve the 1's from each of the lines and append them to the end. The resultant file would look like this:
14.23,1.71,2.43,15.6,127,2.8,3.06,.28,2.29,5.64,1.04,3.92,1065,1
13.2,1.78,2.14,11.2,100,2.65,2.76,.26,1.28,4.38,1.05,3.4,1050,1
13.16,2.36,2.67,18.6,101,2.8,3.24,.3,2.81,5.68,1.03,3.17,1185,1
14.37,1.95,2.5,16.8,113,3.85,3.49,.24,2.18,7.8,.86,3.45,1480,1
13.24,2.59,2.87,21,118,2.8,2.69,.39,1.82,4.32,1.04,2.93,735,1
I was looking for an elegant way to do this.
Actually I tried it like
:%s/$/,/g
And then
:%s/$/^./g
But I could not make it to work.
EDIT : Well, actually I made one mistake in my question. In the data-file, the first character is not always 1, they are mixture of 1, 2 and 3. So, from all the answers from this questions, I came up with the solution --
:%s/^\([1-3]\),\(.*\)/\2,\1/g
and it is working now.
A regular expression that doesn't care which number, its digits, or separator you've used. That is, this would work for lines that have both 1 as their first number, or 114:
:%s/\([0-9]*\)\(.\)\(.*\)/\3\2\1/
Explanation:
:%s// - Substitute every line (%)
\(<something>\) - Extract and store to \n
[0-9]* - A number 0 or more times
. - Every char, in this case,
.* - Every char 0 or more times
\3\2\1 - Replace what is captured with \(\)
So: Cut up 1 , <the rest> to \1, \2 and \3 respectively, and reorder them.
This
:%s/^1,//
:%s/$/,1/
could be somewhat simpler to understand.
:%s/^1,\(.*\)/\1,1/
This will do the replacement on each line in the file. The \1 replaces everything captured by the (.*)
:%s/1,\(.*$\)/\1,1/gc
.........................
You could also solve this one using a macro. First, think about how to delete the 1, from the start of a line and append it to the end:
0 go the the start of the line
df, delete everything to and including the first ,
A,<ESC> append a comma to the end of the line
p paste the thing you deleted with df,
x delete the trailing comma
So, to sum it up, the following will convert a single line:
0df,A,<ESC>px
Now if you'd like to apply this set of modifications to all the lines, you will first need to record them:
qj start recording into the 'j' register
0df,A,<ESC>px convert a single line
j go to the next line
q stop recording
Finally, you can execute the macro anytime you want using #j, or convert your entire file with 99#j (using a higher number than 99 if you have more than 99 lines).
Here's the complete version:
qj0df,A,<ESC>pxjq99#j
This one might be easier to understand than the other solutions if you're not used to regular expressions!

Regular Expression over multiple lines

I'm stuck with this for several hours now and cycled through a wealth of different tools to get the job done. Without success. It would be fantastic, if someone could help me out with this.
Here is the problem:
I have a very large CSV file (400mb+) that is not formatted correctly. Right now it looks something like this:
This is a long abstract describing something. What follows is the tile for this sentence."
,Title1
This is another sentence that is running on one line. On the next line you can find the title.
,Title2
As you can probably see the titles ",Title1" and ",Title2" should actually be on the same line as the foregoing sentence. Then it would look something like this:
This is a long abstract describing something. What follows is the tile for this sentence.",Title1
This is another sentence that is running on one line. On the next line you can find the title.,Title2
Please note that the end of the sentence can contain quotes or not. In the end they should be replaced too.
Here is what I came up with so far:
sed -n '1h;1!H;${;g;s/\."?.*,//g;p;}' out.csv > out1.csv
This should actually get the job done of matching the expression over multiple lines. Unfortunately it doesn't :)
The expression is looking for the dot at the end of the sentence and the optional quotes plus a newline character that I'm trying to match with .*.
Help much appreciated. And it doesn't really matter what tool gets the job done (awk, perl, sed, tr, etc.).
Multiline in sed isn't necessarily tricky per se, it's just that it uses commands most people aren't familiar with and have certain side effects, like delimiting the current line from the next line with a '\n' when you use 'N' to append the next line to the pattern space.
Anyway, it's much easier if you match on a line that starts with a comma to decide whether or not to remove the newline, so that's what I did here:
sed 'N;/\n,/s/"\? *\n//;P;D' title_csv
Input
$ cat title_csv
don't touch this line
don't touch this line either
This is a long abstract describing something. What follows is the tile for this sentence."
,Title1
seriously, don't touch this line
This is another sentence that is running on one line. On the next line you can find the title.
,Title2
also, don't touch this line
Output
$ sed 'N;/\n,/s/"\? *\n//;P;D' title_csv
don't touch this line
don't touch this line either
This is a long abstract describing something. What follows is the tile for this sentence.,Title1
seriously, don't touch this line
This is another sentence that is running on one line. On the next line you can find the title.,Title2
also, don't touch this line
Yours works with a couple of small changes:
sed -n '1h;1!H;${;g;s/\."\?\n,//g;p;}' inputfile
The ? needs to be escaped and . doesn't match newlines.
Here's another way to do it which doesn't require using the hold space:
sed -n '${p;q};N;/\n,/{s/"\?\n//p;b};P;D' inputfile
Here is a commented version:
sed -n '
$ # for the last input line
{
p; # print
q # and quit
};
N; # otherwise, append the next line
/\n,/ # if it starts with a comma
{
s/"\?\n//p; # delete an optional comma and the newline and print the result
b # branch to the end to read the next line
};
P; # it doesn't start with a comma so print it
D # delete the first line of the pair (it's just been printed) and loop to the top
' inputfile