Globally replace text in several files: Difference between revisions

→‎{{header|Go}}: Add Fortran.
No edit summary
(→‎{{header|Go}}: Add Fortran.)
Line 454:
0</lang>
 
=={{header|Fortran}}==
This is in the style of F77 and solves the usual problem of "how long is a piece of string" by choosing a size that is surely long enough. Thus, CHARACTER*6666 ALINE. Fortran 2003 allows the alteration of the length of a character variable, but, unless there is a facility whereby in something like <code>READ(F,11) ALINE</code> the size of ALINE is adjusted to suit the record being read, this doesn't help much. The Q format code allows discovery of the length of a record as it is being read, and so only the required portion, ALINE(1:L), of an input record is placed and trailing spaces out to 6666 are not supplied nor need they be scanned. Thus an input record that has trailing spaces will have them preserved - unless the text replacement changes spaces...
 
The search is done by using the supplied INDEX function which alas rarely has an option to specify the starting point via an additional (optional?) parameter. So this must be done via <code>INDEX(ALINE(L1:L),THIS)</code> and then one must carefully consider offsets and the like while counting on fingers and becoming confused. The expression ALINE(L1:L) does ''not'' create a new string variable by copying the specified text, it works (or should work!) via offsets into ALINE. Similarly, there is no attempt to concatenate an output string to write in one go as that too would involve copying text about. Though WRITE statements involve no small overhead in themselves...
 
The file to be altered cannot be changed "in-place", as by writing back an altered record even if the text replacement does not involve a change in length because such a facility is not available for text files that are read and written sequentially only. So, the altered content has to be written to a temporary file (or perhaps could be held in a capacious memory) which is then read back to overwrite the original file. It would be safer to rename the original file and write to a new version, but Fortran typically does not have access to any file renaming facilities. So, overwrite it is, which is actually a file delete followed by a write.
 
Once equipped with a subroutine that applies a specified change to a named disc file, there is no difficulty in invoking it for a horde of disc files. A more civilised routine might make reports about the files assaulted and the number of changes, and also be prepared to report various oddities such as a file being available but not for WRITE. It is for this reason that the source file is opened with READWRITE even though it at that stage is only going to be read from.<lang Fortran> SUBROUTINE FILEHACK(FNAME,THIS,THAT) !Attacks a file!
CHARACTER*(*) FNAME !The name of the file, presumed to contain text.
CHARACTER*(*) THIS !The text sought in each record.
CHARACTER*(*) THAT !Its replacement, should it be found.
INTEGER F,T !Mnemonics for file unit numbers.
PARAMETER (F=66,T=67) !These should do.
INTEGER L !A length
CHARACTER*6666 ALINE !Surely sufficient?
LOGICAL AHIT !Could count them, but no report is called for.
INQUIRE(FILE = FNAME, EXIST = AHIT) !This mishap is frequent, so attend to it.
IF (.NOT.AHIT) RETURN !Nothing can be done!
OPEN (F,FILE=FNAME,STATUS="OLD",ACTION="READWRITE") !Grab the source file.
OPEN (T,STATUS="SCRATCH") !Request a temporary file.
AHIT = .FALSE. !None found so far.
Chew through the input, replacing THIS by THAT while writing to the temporary file..
10 READ (F,11,END = 20) L,ALINE(1:MIN(L,LEN(ALINE))) !Grab a record.
IF (L.GT.LEN(ALINE)) STOP "Monster record!" !Perhaps unmanageable.
11 FORMAT (Q,A) !Obviously, Q = length of characters unread in the record.
L1 = 1 !Start at the start.
12 L2 = INDEX(ALINE(L1:L),THIS) !Look from L1 onwards.
IF (L2.LE.0) THEN !A hit?
WRITE (T,13) ALINE(L1:L) !No. Finish with the remainder of the line.
13 FORMAT (A) !Thus finishing the output line.
GO TO 10 !And try for the next record.
END IF !So much for not finding THIS.
14 L2 = L1 + L2 - 2 !Otherwise, THIS is found, starting at L1.
WRITE (T,15) ALINE(L1:L2) !So roll the text up to the match, possibly none.
15 FORMAT (A,$) !But not ending the record.
WRITE (T,15) THAT !Because THIS is replaced by THAT.
AHIT = .TRUE. !And we've found at least one match.
L1 = L2 + LEN(THIS) + 1 !Finger the first character beyond the matching THIS.
IF (L - L1 + 1..GE. LEN(THIS)) GO TO 12 !Might another search succeed?
WRITE (T,13) ALINE(L1:L) !Nope. Finish the line with the tail end.
GO TO 10 !And try for another record.
Copy the temporary file back over the source file. Hope for no mishap and data loss!
20 IF (AHIT) THEN !If there were no hits, there is nothing to do.
CLOSE (F) !Oh well.
REWIND T !Go back to the start.
OPEN (F,FILE="new"//FNAME,STATUS = "REPLACE",ACTION = "WRITE") !Overwrite...
21 READ (T,11,END = 22) L,ALINE(1:MIN(L,LEN(ALINE))) !Grab a line.
IF (L.GT.LEN(ALINE)) STOP "Monster changed record!" !Once you start checking...
WRITE (F,13) ALINE(1:L) !In case LEN(THAT) > LEN(THIS)
GO TO 21 !Go grab the next line.
END IF !So much for the replacement of the file.
22 CLOSE(T) !Finished: it will vanish.
CLOSE(F) !Hopefully, the buffers will be written.
END !So much for that.
 
PROGRAM ATTACK
INTEGER N
PARAMETER (N = 6) !More than one, anyway.
CHARACTER*48 VICTIM(N) !Alternatively, the file names could be read from a file
DATA VICTIM/ !Along with the target and replacement texts in each case.
1 "StaffStory.txt",
2 "Accounts.dat",
3 "TravelAgent.txt",
4 "RemovalFirm.dat",
5 "Addresses.txt",
6 "SongLyrics.txt"/ !Invention flags.
 
DO I = 1,N !So, step through the list.
CALL FILEHACK(VICTIM(I),"Goodbye London!","Hello New York!") !One by one.
END DO !On to the next.
 
END</lang>
 
=={{header|Go}}==
<lang go>package main
1,220

edits