Globally replace text in several files: Difference between revisions
Line 706: | Line 706: | ||
print(line.replace('Goodbye London!', 'Hello New York!'), end='') |
print(line.replace('Goodbye London!', 'Hello New York!'), end='') |
||
</lang> |
</lang> |
||
=={{header|Racket}}== |
|||
Code wrapped in a convenient script: |
|||
<lang racket> |
|||
#!/usr/bin/env racket |
|||
#lang racket |
|||
(define from-string #f) |
|||
(define to-string #f) |
|||
(command-line |
|||
#:once-each |
|||
[("-f") from "Text to remove" (set! from-string from)] |
|||
[("-t") to "Text to put instead" (set! to-string to)] |
|||
#:args files |
|||
(unless from-string (error "No `from' string specified")) |
|||
(unless to-string (error "No `to' string specified")) |
|||
(when (null? files) (error "No files given")) |
|||
(define from-rx (regexp (regexp-quote from-string))) |
|||
(for ([file files]) |
|||
(printf "Editing ~a..." file) (flush-output) |
|||
(define text1 (file->string file)) |
|||
(define text2 (regexp-replace* from-rx text1 to-string)) |
|||
(if (equal? text1 text2) |
|||
(printf " no change\n") |
|||
(begin (display-to-file text2 file #:exists 'replace) |
|||
(printf " modified copy saved in place\n"))))) |
|||
</lang> |
|||
Sample run: |
|||
<pre> |
|||
$ ./replace -h |
|||
replace [ <option> ... ] [<files>] ... |
|||
where <option> is one of |
|||
-f <from> : Text to remove |
|||
-t <to> : Text to put instead |
|||
--help, -h : Show this help |
|||
-- : Do not treat any remaining argument as a switch (at this level) |
|||
Multiple single-letter switches can be combined after one `-'; for |
|||
example: `-h-' is the same as `-h --' |
|||
$ ./replace -f "Goodbye London!" "Hello New York!" file* |
|||
Editing file1... no change |
|||
Editing file2... modified copy saved in place |
|||
Editing file3... modified copy saved in place |
|||
</pre> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
Revision as of 06:40, 21 May 2013
You are encouraged to solve this task according to the task description, using any language you may know.
The task is to replace every occurring instance of a piece of text in a group of text files with another one. For this task we want to replace the text "Goodbye London!" with "Hello New York!" for a list of files.
Ada
<lang Ada>with Ada.Strings.Unbounded, Ada.Text_IO, Ada.Command_Line, Ada.Directories;
procedure Global_Replace is
subtype U_String is Ada.Strings.Unbounded.Unbounded_String; function "+"(S: String) return U_String renames Ada.Strings.Unbounded.To_Unbounded_String; function "-"(U: U_String) return String renames Ada.Strings.Unbounded.To_String;
procedure String_Replace(S: in out U_String; Pattern, Replacement: String) is -- example: if S is "Mary had a XX lamb", then String_Replace(S, "X", "little"); -- will turn S into "Mary had a littlelittle lamb" -- and String_Replace(S, "Y", "small"); will not change S
Index : Natural; begin loop Index := Ada.Strings.Unbounded.Index(Source => S, Pattern => Pattern); exit when Index = 0; Ada.Strings.Unbounded.Replace_Slice (Source => S, Low => Index, High => Index+Pattern'Length-1, By => Replacement); end loop; end String_Replace;
procedure File_Replace(Filename: String; Pattern, Replacement: String) is -- applies String_Rplace to each line in the file with the given Filename -- propagates any exceptions, when, e.g., the file does not exist
I_File, O_File: Ada.Text_IO.File_Type; Line: U_String; Tmp_Name: String := Filename & ".tmp"; -- name of temporary file; if that file already exists, it will be overwritten begin Ada.Text_IO.Open(I_File, Ada.Text_IO.In_File, Filename); Ada.Text_IO.Create(O_File, Ada.Text_IO.Out_File, Tmp_Name); while not Ada.Text_IO.End_Of_File(I_File) loop Line := +Ada.Text_IO.Get_Line(I_File); String_Replace(Line, Pattern, Replacement); Ada.Text_IO.Put_Line(O_File, -Line); end loop; Ada.Text_IO.Close(I_File); Ada.Text_IO.Close(O_File); Ada.Directories.Delete_File(Filename); Ada.Directories.Rename(Old_Name => Tmp_Name, New_Name => Filename); end File_Replace;
Pattern: String := Ada.Command_Line.Argument(1); Replacement: String := Ada.Command_Line.Argument(2);
begin
Ada.Text_IO.Put_Line("Replacing """ & Pattern & """ by """ & Replacement & """ in" & Integer'Image(Ada.Command_Line.Argument_Count - 2) & " files."); for I in 3 .. Ada.Command_Line.Argument_Count loop File_Replace(Ada.Command_Line.Argument(I), Pattern, Replacement); end loop;
end Global_Replace;</lang>
Ouput:
> ls ?.txt 1.txt 2.txt x.txt y.txt > more 2.txt This is a text. "Goodbye London!" "Goodbye London!" "Byebye London!" "Byebye London!" "Byebye London!" > ./global_replace "Goodbye London" "Hello New York" ?.txt Replacing "Goodbye London" by "Hello New York" in 4 files. > more 2.txt This is a text. "Hello New York!" "Hello New York!" "Byebye London!" "Byebye London!" "Byebye London!"
AutoHotkey
<lang AutoHotkey>SetWorkingDir %A_ScriptDir% ; Change the working directory to the script's location listFiles := "a.txt|b.txt|c.txt" ; Define a list of files in the current working directory loop, Parse, listFiles, | { ; The above parses the list based on the | character fileread, contents, %A_LoopField% ; Read the file fileDelete, %A_LoopField% ; Delete the file stringReplace, contents, contents, Goodbye London!, Hello New York!, All ; replace all occurrences fileAppend, %contents%, %A_LoopField% ; Re-create the file with new contents } </lang>
AWK
<lang AWK>
- syntax: GAWK -f GLOBALLY_REPLACE_TEXT_IN_SEVERAL_FILES.AWK filename(s)
BEGIN {
old_text = "Goodbye London!" new_text = "Hello New York!"
} BEGINFILE {
nfiles_in++ text_found = 0 delete arr
} { if (gsub(old_text,new_text,$0) > 0) {
text_found++ } arr[FNR] = $0
} ENDFILE {
if (text_found > 0) { nfiles_out++ close(FILENAME) for (i=1; i<=FNR; i++) { printf("%s\n",arr[i]) >FILENAME } }
} END {
printf("files: %d read, %d updated\n",nfiles_in,nfiles_out) exit(0)
} </lang>
BASIC
Pass the files on the command line (i.e. global-replace *.txt
).
<lang qbasic>CONST matchtext = "Goodbye London!" CONST repltext = "Hello New York!" CONST matchlen = LEN(matchtext)
DIM L0 AS INTEGER, x AS INTEGER, filespec AS STRING, linein AS STRING
L0 = 1 WHILE LEN(COMMAND$(L0))
filespec = DIR$(COMMAND$(L0)) WHILE LEN(filespec) OPEN filespec FOR BINARY AS 1 linein = SPACE$(LOF(1)) GET #1, 1, linein DO x = INSTR(linein, matchtext) IF x THEN linein = LEFT$(linein, x - 1) & repltext & MID$(linein, x + matchlen) ' If matchtext and repltext are of equal length (as in this example) ' then you can replace the above line with this: ' MID$(linein, x) = repltext ' This is somewhat more efficient than having to rebuild the string. ELSE EXIT DO END IF LOOP ' If matchtext and repltext are of equal length (as in this example), or repltext ' is longer than matchtext, you could just write back to the file while it's open ' in BINARY mode, like so: ' PUT #1, 1, linein ' But since there's no way to reduce the file size via BINARY and PUT, we do this: CLOSE OPEN filespec FOR OUTPUT AS 1 PRINT #1, linein; CLOSE filespec = DIR$ WEND L0 += 1
WEND</lang>
BBC BASIC
<lang bbcbasic> FindThis$ = "Goodbye London!"
ReplaceWith$ = "Hello New York!" DIM Files$(3) Files$() = "C:\test1.txt", "C:\test2.txt", "C:\test3.txt", "C:\test4.txt" FOR f% = 0 TO DIM(Files$(),1) infile$ = Files$(f%) infile% = OPENIN(infile$) IF infile%=0 ERROR 100, "Failed to open file " + infile$ tmpfile$ = @tmp$+"replace.txt" tmpfile% = OPENOUT(tmpfile$) WHILE NOT EOF#infile% INPUT #infile%, a$ IF ASCa$=10 a$ = MID$(a$,2) l% = LEN(FindThis$) REPEAT here% = INSTR(a$, FindThis$) IF here% a$ = LEFT$(a$,here%-1) + ReplaceWith$ + MID$(a$,here%+l%) UNTIL here% = 0 PRINT #tmpfile%, a$ : BPUT #tmpfile%,10 ENDWHILE CLOSE #infile% CLOSE #tmpfile% OSCLI "DEL """ + infile$ + """" OSCLI "REN """ + tmpfile$ + """ """ + infile$ + """" NEXT END</lang>
C
<lang C>#include <stdio.h>
- include <stdlib.h>
- include <stddef.h>
- include <string.h>
- include <sys/types.h>
- include <fcntl.h>
- include <sys/stat.h>
- include <unistd.h>
- include <err.h>
- include <string.h>
char * find_match(const char *buf, const char * buf_end, const char *pat, size_t len) { ptrdiff_t i; char *start = buf; while (start + len < buf_end) { for (i = 0; i < len; i++) if (start[i] != pat[i]) break;
if (i == len) return (char *)start; start++; } return 0; }
int replace(const char *from, const char *to, const char *fname) {
- define bail(msg) { warn(msg" '%s'", fname); goto done; }
struct stat st; int ret = 0; char *buf = 0, *start, *end; size_t len = strlen(from), nlen = strlen(to); int fd = open(fname, O_RDWR);
if (fd == -1) bail("Can't open"); if (fstat(fd, &st) == -1) bail("Can't stat"); if (!(buf = malloc(st.st_size))) bail("Can't alloc"); if (read(fd, buf, st.st_size) != st.st_size) bail("Bad read");
start = buf; end = find_match(start, buf + st.st_size, from, len); if (!end) goto done; /* no match found, don't change file */
ftruncate(fd, 0); lseek(fd, 0, 0); do { write(fd, start, end - start); /* write content before match */ write(fd, to, nlen); /* write replacement of match */ start = end + len; /* skip to end of match */ /* find match again */ end = find_match(start, buf + st.st_size, from, len); } while (end);
/* write leftover after last match */ if (start < buf + st.st_size) write(fd, start, buf + st.st_size - start);
done: if (fd != -1) close(fd); if (buf) free(buf); return ret; }
int main() { const char *from = "Goodbye, London!"; const char *to = "Hello, New York!"; const char * files[] = { "test1.txt", "test2.txt", "test3.txt" }; int i;
for (i = 0; i < sizeof(files)/sizeof(char*); i++) replace(from, to, files[i]);
return 0; }</lang>
C++
<lang cpp>#include <fstream>
- include <iterator>
- include <boost/regex.hpp>
- include <string>
- include <iostream>
int main( int argc , char *argv[ ] ) {
boost::regex to_be_replaced( "Goodbye London\\s*!" ) ; std::string replacement( "Hello New York!" ) ; for ( int i = 1 ; i < argc ; i++ ) { std::ifstream infile ( argv[ i ] ) ; if ( infile ) {
std::string filetext( (std::istreambuf_iterator<char>( infile )) , std::istreambuf_iterator<char>( ) ) ; std::string changed ( boost::regex_replace( filetext , to_be_replaced , replacement )) ; infile.close( ) ; std::ofstream outfile( argv[ i ] , std::ios_base::out | std::ios_base::trunc ) ; if ( outfile.is_open( ) ) { outfile << changed ; outfile.close( ) ; }
} else
std::cout << "Can't find file " << argv[ i ] << " !\n" ;
} return 0 ;
}</lang>
D
<lang d>import std.file, std.array;
void main() {
auto from = "Goodbye London!", to = "Hello, New York!"; foreach (fn; "a.txt b.txt c.txt".split()) { write(fn, replace(cast(string)read(fn), from, to)); }
}</lang>
Go
<lang go>package main
import (
"bytes" "io/ioutil" "log" "os"
)
func main() {
gRepNFiles("Goodbye London!", "Hello New York!", []string{ "a.txt", "b.txt", "c.txt", })
}
func gRepNFiles(olds, news string, files []string) {
oldb := []byte(olds) newb := []byte(news) for _, fn := range files { if err := gRepFile(oldb, newb, fn); err != nil { log.Println(err) } }
}
func gRepFile(oldb, newb []byte, fn string) (err error) {
var f *os.File if f, err = os.OpenFile(fn, os.O_RDWR, 0); err != nil { return } defer func() { if cErr := f.Close(); err == nil { err = cErr } }() var b []byte if b, err = ioutil.ReadAll(f); err != nil { return } if bytes.Index(b, oldb) < 0 { return } r := bytes.Replace(b, oldb, newb, -1) if err = f.Truncate(0); err != nil { return } _, err = f.WriteAt(r, 0) return
}</lang>
Haskell
The module Data.List provides some useful functions: tails (constructs substrings dropping elements from the head of the list), isPrefixOf (checks if a string matches the beginning of another one) and elemIndices (gets the list indices of all elements matching a value). This code doesn't rewrite the files, it just returns the changes made to the contents of the files. <lang Haskell>import Data.List (tails, elemIndices, isPrefixOf)
replace :: String -> String -> String -> String replace [] _ xs = xs replace _ [] xs = xs replace _ _ [] = [] replace a b xs = replAll
where -- make substrings, dropping one element each time xtails = tails xs -- what substrings begin with the string to replace? -- get their indices matches = elemIndices True $ map (isPrefixOf a) xtails -- replace one occurrence repl ys n = take n ys ++ b ++ drop (n + length b) ys -- replace all occurrences consecutively replAll = foldl repl xs matches
replaceInFiles a1 a2 files = do
f <- mapM readFile files return $ map (replace a1 a2) f
</lang> This other version is more effective because it processes the string more lazily, replacing the text as it consumes the input string (the previous version was stricter because of "matches" traversing the whole list; that would force the whole string into memory, which could cause the system to run out of memory with large text files). <lang Haskell>replace _ _ [] = [] replace a b xx@(x:xs) =
if isPrefixOf a xx then b ++ replace a b (drop (length a) xx) else x : replace a b xs
</lang> Example:
File "t1.txt" contains: "Goodbye London! This is text 1." File "t2.txt" contains: "This is text 2. Goodbye London! Now we repeat: Goodbye London! And that's all." replaceInFiles "Goodbye London!" "Hello New York!" ["t1.txt", "t2.txt"]
Output:
["Hello New York! This is text 1.\n","This is text 2. Hello New York! Now we repeat: Hello New York! And that's all.\n"]
Icon and Unicon
This example uses the Unicon stat function. It can be rewritten for Icon to aggregate the file in a reads loop. <lang Icon>procedure main() globalrepl("Goodbye London","Hello New York","a.txt","b.txt") # variable args for files end
procedure globalrepl(old,new,files[])
every fn := !files do
if s := reads(f := open(fn,"bu"),stat(f).size) then { writes(seek(f,1),replace(s,old,new)) close(f) } else write(&errout,"Unable to open ",fn)
end
link strings # for replace</lang>
J
If files
is a variable with the desired list of file names:
<lang j>require'strings' (1!:2~rplc&('Goodbye London!';'Hello New York!')@(1!:1))"0 files</lang>
Java
<lang Java>
import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter;
public class GlobalReplace { public static void main(String[] args) { //Enter names of the files here String[] filename={"foobar0.txt","foobar1.txt","foobar2.txt","foobar3.txt","foobar4.txt"}; //Enter text to replace here String from="Goodbye London!"; //Enter replacing text here String to="Hello New York!";
GlobalReplace now=new GlobalReplace(); for(int i=0;i<filename.length;i++) now.delete(filename[i],from,to); } void delete(String filename, String from, String to) { try { BufferedReader br=new BufferedReader(new FileReader(filename));
//String buffer to store contents of the file StringBuffer sb=new StringBuffer("");
String line;
while((line=br.readLine())!=null) { //If from appears, replace it if(line.contains(from)) sb.append(line.replace(from, to)+"\n"); else sb.append(line+"\n");
} br.close();
FileWriter fw=new FileWriter(new File(filename)); //Write entire string buffer into the file fw.write(sb.toString()); fw.close(); } catch (Exception e) { System.out.println("Something went horribly wrong: "+e.getMessage()); } } }
</lang>
Liberty BASIC
<lang lb> nomainwin
file$( 1) ="data1.txt" file$( 2) ="data2.txt" file$( 3) ="data3.txt"
for i =1 to 3
open file$( i) for input as #i orig$ =input$( #i, lof( #i)) close #i
dummy$ =FindReplace$( orig$, "Goodbye London!", "Hello New York!", 1)
open "RC" +file$( i) for output as #o #o dummy$; close #o
next i
end
function FindReplace$( FindReplace$, find$, replace$, replaceAll) ' Target string, string to find, string to replace it with, flag 0/1 for 'replace all occurrences'.
if ( ( FindReplace$ <>"") and ( find$ <>"") ) then fLen =len( find$) rLen =len( replace$) do fPos =instr( FindReplace$, find$, fPos) if not( fPos) then exit function pre$ =left$( FindReplace$, fPos -1) post$ =mid$( FindReplace$, fPos +fLen) FindReplace$ =pre$ +replace$ +post$ fPos =fPos +( rLen -fLen) +1 loop while (replaceAll) end if
end function </lang>
Lua
<lang lua>filenames = { "f1.txt", "f2.txt" }
for _, fn in pairs( filenames ) do
fp = io.open( fn, "r" ) str = fp:read( "*all" ) str = string.gsub( str, "Goodbye London!", "Hello New York!" ) fp:close()
fp = io.open( fn, "w+" ) fp:write( str ) fp:close()
end</lang>
OpenEdge/Progress
<lang progress>FUNCTION replaceText RETURNS LOGICAL (
i_cfile_list AS CHAR, i_cfrom AS CHAR, i_cto AS CHAR
):
DEF VAR ii AS INT. DEF VAR lcfile AS LONGCHAR.
DO ii = 1 TO NUM-ENTRIES( i_cfile_list ): COPY-LOB FROM FILE ENTRY( ii, i_cfile_list ) TO lcfile. lcfile = REPLACE( lcfile, i_cfrom, i_cto ). COPY-LOB FROM lcfile TO FILE ENTRY( ii, i_cfile_list ). END.
END FUNCTION. /* replaceText */
replaceText(
"a.txt,b.txt,c.txt", "Goodbye London!", "Hello New York!"
).</lang>
Pascal
<lang pascal>Program StringReplace;
uses
Classes, StrUtils;
const
fileName: array[1..3] of string = ('a.txt', 'b.txt', 'c.txt'); matchText = 'Goodbye London!'; replaceText = 'Hello New York!';
var
AllText: TStringlist; i, j: integer;
begin
for j := low(fileName) to high(fileName) do begin AllText := TStringlist.Create; AllText.LoadFromFile(fileName[j]); for i := 0 to AllText.Count-1 do AllText.Strings[i] := AnsiReplaceStr(AllText.Strings[i], matchText, replaceText); AllText.SaveToFile(fileName[j]); AllText.Destroy; end;
end.</lang>
Perl
<lang bash>perl -pi -e "s/Goodbye London\!/Hello New York\!/g;" a.txt b.txt c.txt</lang>
Perl 6
Current Perl 6 implementations do not yet support the -i flag for editing files in place, so we roll our own (rather unsafe) version:
<lang perl6>slurp($_).subst('Goodbye London!', 'Hello New York!', :g) ==> spurt($_)
for <a.txt b.txt c.txt>;</lang>
PicoLisp
<lang PicoLisp>(for File '(a.txt b.txt c.txt)
(call 'mv File (tmp File)) (out File (in (tmp File) (while (echo "Goodbye London!") (prin "Hello New York!") ) ) ) )</lang>
PowerBASIC
<lang powerbasic>$matchtext = "Goodbye London!" $repltext = "Hello New York!"
FUNCTION PBMAIN () AS LONG
DIM L0 AS INTEGER, filespec AS STRING, linein AS STRING
L0 = 1 WHILE LEN(COMMAND$(L0)) filespec = DIR$(COMMAND$(L0)) WHILE LEN(filespec) OPEN filespec FOR BINARY AS 1 linein = SPACE$(LOF(1)) GET #1, 1, linein ' No need to jump through FB's hoops here... REPLACE $matchtext WITH $repltext IN linein PUT #1, 1, linein SETEOF #1 CLOSE filespec = DIR$ WEND INCR L0 WEND
END FUNCTION</lang>
PureBasic
<lang PureBasic>Procedure GRTISF(List File$(), Find$, Replace$)
Protected Line$, Out$, OutFile$, i ForEach File$() fsize=FileSize(File$()) If fsize<=0: Continue: EndIf If ReadFile(0, File$()) i=0 ; ; generate a temporary file in a safe way Repeat file$=GetTemporaryDirectory()+base$+"_"+Str(i)+".tmp" i+1 Until FileSize(file$)=-1 i=CreateFile(FileID, file$) If i ; Copy the infile to the outfile while replacing any needed text While Not Eof(0) Line$=ReadString(0) Out$=ReplaceString(Line$,Find$,Replace$) WriteString(1,Out$) Wend CloseFile(1) EndIf CloseFile(0) If i ; If we made a new file, copy it back. CopyFile(file$, File$()) DeleteFile(file$) EndIf EndIf Next
EndProcedure</lang> Implementation
NewList Xyz$() AddElement(Xyz$()): Xyz$()="C:\\a.txt" AddElement(Xyz$()): Xyz$()="C:\\b.txt" AddElement(Xyz$()): Xyz$()="D:\\c.txt" GRTISF(Xyz$(), "Goodbye London", "Hello New York")
Python
From Python docs. (Note: in-place editing does not work for MS-DOS 8+3 filesystems.).
<lang python>import fileinput
for line in fileinput.input(inplace=True):
print(line.replace('Goodbye London!', 'Hello New York!'), end=)
</lang>
Racket
Code wrapped in a convenient script: <lang racket>
- !/usr/bin/env racket
- lang racket
(define from-string #f) (define to-string #f)
(command-line
#:once-each [("-f") from "Text to remove" (set! from-string from)] [("-t") to "Text to put instead" (set! to-string to)] #:args files (unless from-string (error "No `from' string specified")) (unless to-string (error "No `to' string specified")) (when (null? files) (error "No files given")) (define from-rx (regexp (regexp-quote from-string))) (for ([file files]) (printf "Editing ~a..." file) (flush-output) (define text1 (file->string file)) (define text2 (regexp-replace* from-rx text1 to-string)) (if (equal? text1 text2) (printf " no change\n") (begin (display-to-file text2 file #:exists 'replace) (printf " modified copy saved in place\n")))))
</lang> Sample run:
$ ./replace -h replace [ <option> ... ] [<files>] ... where <option> is one of -f <from> : Text to remove -t <to> : Text to put instead --help, -h : Show this help -- : Do not treat any remaining argument as a switch (at this level) Multiple single-letter switches can be combined after one `-'; for example: `-h-' is the same as `-h --' $ ./replace -f "Goodbye London!" "Hello New York!" file* Editing file1... no change Editing file2... modified copy saved in place Editing file3... modified copy saved in place
REXX
version 1
This example works under "DOS" and/or "DOS" under Microsoft Windows.
Filenames that contain blanks should have their blanks replaced with commas.
<lang rexx>/*REXX program to read the files specified and globally replace a string*/
old = 'Goodbye London!' /*old text to be replaced. */
new = 'Hello New York!' /*new text used for replacement. */
parse arg fileList; files=words(fileList); pad=left(,20)
hdr='────── file' /*eyecatcher.*/ do f=1 for files; aFile=translate(word(fileList,f),,','); say; say say hdr' is being read: ' aFile pad "("f 'out of' files "files)." call linein aFile,1,0 /*position the file for input. */ changes=0 /*# changes in the file (so far).*/
do j=1 while lines(aFile)\==0 /*read a file (if it exists). */ @.j = linein(aFile) /*read a record from the file. */ if pos(old,@.j)==0 then iterate /*Anything to change? No, skip.*/ changes = changes+1 /*bump the change counter. */ @.j = changestr(old,@.j,new) /*change this record, old ──► new*/ end /*j*/
say hdr ' was read: ' aFile", with " j-1 'records.' if changes == 0 then do say hdr ' not changed: ' aFile iterate /*f*/ end call lineout aFile,,1 /*position the file for output. */ say hdr 'being changed: ' aFile do r=1 for j-1; call lineout aFile,@.r; end /*re-write file.*/ say hdr 'was changed: ' aFile " with" changes 'lines changed.' end /*f*/ /*stick a fork in it, we're done.*/</lang>
Some older REXXes don't have a changestr bif, so one is included here ──► CHANGESTR.REX.
output when using the input of: one.txt two.txt
────── file is being read: one.txt (1 out of 2 files). ────── file was read: one.txt, with 13 records. ────── file being changed: one.txt ────── file was changed: one.txt with 2 lines changed. ────── file is being read: two.txt (2 out of 2 files). ────── file was read: two.txt, with 59 records. ────── file being changed: two.txt ────── file was changed: two.txt with 6 lines changed.
Version 2
considering file ids that contain blanks
<lang rexx>/* REXX ***************************************************************
- Copy all files *.txt to *.rpl
- replacing all occurrences of old by new
- Execute in the directory containing the files to ne processed
- 16.01.2013 Walter Pachl
- ...if file names contain blanks
- /
Parse Arg a If a='?' Then Do
Do i=2 To 5 Say substr(sourceline(i),3) End Exit End
'dir *.rpl' Say 'May I erase *.rpl?' Parse Upper Pull answer If answer='Y' | answer='J' Then
'erase *.rpl'
Else Do
Say 'Giving up..' Exit End
old='Goodbye London!' new='Hello New York!' dir='dir.dir' 'dir *.* >' dir Do While lines(dir)>0
Parse Value linein(dir) With 37 f Select When f= |, left(f,1)='.' |, pos(' Bytes',f)>0 Then Iterate When right(f,4)='.txt' Then Call replace Otherwise Say left(f,50) 'not eligible for replacing' End End
Exit
replace: /* REXX ***************************************************************
- Copy a file fn.txt to fn.rpl
- replacing all occurrences of old by new
- /
oid=fn(f)'.rpl' cnt.=0 Do ii=1 By 1 While lines(f)>0
l=linein(f) ol=repl(l,new,old) Call lineout oid,ol End
Call lineout f Call lineout oid Select
When cnt.0changes=0 Then Do 'erase' oid Say left(f,50) 'no changes' End When cnt.0changes=1 Then Say left(f,50) '1 change' Otherwise Say left(f '->' oid,50) cnt.0changes 'changes' End
Return
fn: Procedure /* REXX ***************************************************************
- Get the file name of a file id
- /
parse Arg fid Parse Var fid fn '.' ft Return fn
repl: Procedure Expose cnt. /* REXX ***************************************************************
- Replace an old string by a new one
- /
Parse Arg s,new,old ol= Do Until p=0 p=pos(old,s) If p>0 Then Do ol=ol||left(s,p-1)||new s=substr(s,p+length(old)) cnt.0changes=cnt.0changes+1 End Else ol=ol||s End Return ol</lang>
Sample output:
Datenträger in Laufwerk Z: ist H Volumeseriennummer: FE17-3A89 Verzeichnis von Z:\glr 16.01.2013 09:55 283 input.rpl 16.01.2013 09:55 352 input2.rpl 16.01.2013 09:55 283 input file.rpl 3 Datei(en), 918 Bytes 0 Verzeichnis(se), 3.993.468.928 Bytes frei May I erase *.rpl? ----> here I entered y input.txt -> input.rpl 1 change input.nix not eligible for replacing input2.txt -> input2.rpl 4 changes dir.dir not eligible for replacing input file.txt -> input file.rpl 1 change input3.txt no changes
Ruby
Like Perl:
ruby -pi -e "gsub('Goodbye London!', 'Hello New York!')" a.txt b.txt c.txt
Run BASIC
<lang runbasic>file$(1) ="data1.txt" file$(2) ="data2.txt" file$(3) ="data3.txt"
for i = 1 to 3
open file$(i) for input as #in fileBefore$ = input$( #in, lof( #in)) close #in fileAfter$ = strRep$(fileBefore$, "Goodbye London!", "Hello New York!") open "new_" + file$(i) for output as #out print #out,fileAfter$; close #out
next i end
' -------------------------------- ' string replace - rep str with ' -------------------------------- FUNCTION strRep$(str$,rep$,with$) ln = len(rep$) ln1 = ln - 1 i = 1 while i <= len(str$)
if mid$(str$,i,ln) = rep$ then strRep$ = strRep$ + with$ i = i + ln1 else strRep$ = strRep$ + mid$(str$,i,1) end if
i = i + 1 WEND END FUNCTION</lang>
Seed7
<lang seed7>$ include "seed7_05.s7i";
include "getf.s7i";
const proc: main is func
local var string: fileName is ""; var string: content is ""; begin for fileName range [] ("a.txt", "b.txt", "c.txt") do content := getf(fileName); content := replace(content, "Goodbye London!", "Hello New York!"); putf(fileName, content); end for; end func;</lang>
Tcl
<lang tcl>package require Tcl 8.5 package require fileutil
- Parameters to the replacement
set from "Goodbye London!" set to "Hello New York!"
- Which files to replace
set fileList [list a.txt b.txt c.txt]
- Make a command fragment that performs the replacement on a supplied string
set replacementCmd [list string map [list $from $to]]
- Apply the replacement to the contents of each file
foreach filename $fileList {
fileutil::updateInPlace $filename $replacementCmd
}</lang>
TUSCRIPT
<lang tuscript> $$ MODE TUSCRIPT files="a.txt'b.txt'c.txt"
BUILD S_TABLE search = ":Goodbye London!:"
LOOP file=files
ERROR/STOP OPEN (file,WRITE,-std-) ERROR/STOP CREATE ("scratch",FDF-o,-std-) ACCESS q: READ/STREAM/RECORDS/UTF8 $file s,aken+text/search+eken ACCESS s: WRITE/ERASE/STREAM/UTF8 "scratch" s,aken+text+eken LOOP READ/EXIT q IF (text.ct.search) SET text="Hello New York!" WRITE/ADJUST s ENDLOOP ENDACCESS/PRINT q ENDACCESS/PRINT s ERROR/STOP COPY ("scratch",file) ERROR/STOP CLOSE (file)
ENDLOOP ERROR/STOP DELETE ("scratch") </lang>
TXR
Another use of a screwdriver as a hammer.
The dummy empty output at the end serves a dual purpose. Firstly, without argument clauses following it, the @(next `!mv ...`)
will not actually happen (lazy evaluation!). Secondly, if a txr
script performs no output on standard output, the default action of dumping variable bindings kicks in.
<lang txr>@(next :args) @(collect) @file @(next `@file`) @(freeform) @(coll :gap 0)@notmatch@{match /Goodbye, London!/}@(end)@*tail@/\n/ @(output `@file.tmp`) @(rep)@{notmatch}Hello, New York!@(end)@tail @(end) @(next `!mv @file.tmp @file`) @(output) @(end) @(end)</lang> Run:
$ cat foo.txt aaaGoodbye, London!aaa Goodbye, London! $ cat bar.txt aaaGoodbye, London!aaa Goodbye, London! $ txr replace-files.txr foo.txt bar.txt $ cat foo.txt aaaHello, New York!aaa Hello, New York! $ cat bar.txt aaaHello, New York!aaa Hello, New York!
Run, with no directory permissions:
$ chmod a-w . $ txr replace-files.txr foo.txt bar.txt txr: unhandled exception of type file_error: txr: could not open foo.txt.tmp (error 13/Permission denied) false
Vedit macro language
The list of files is in file "files.lst" which is expected to be in current directory. <lang vedit>File_Open("files.lst") // list of files to process
- 20 = Reg_Free // text register for filename
While(!At_EOF) {
Reg_Copy_Block(#20, Cur_Pos, EOL_Pos) File_Open(@(#20)) Replace("Goodbye London!", "Hello New York!", BEGIN+ALL+NOERR) Buf_Close(NOMSG) Line(1, ERRBREAK)
}
Reg_Empty(#20) // Cleanup Buf_Quit(OK)</lang>
XPL0
<lang XPL0>include c:\cxpl\codes; \intrinsic 'code' declarations string 0; \use zero-terminated strings
func StrLen(A); \Return number of characters in an ASCIIZ string char A; int I; for I:= 0 to -1>>1-1 do
if A(I) = 0 then return I;
func StrFind(A, B); \Search for ASCIIZ string A in string B \Returns address of first occurrence of string A in B, or zero if A is not found char A, B; \strings to be compared int LA, LB, I, J; [LA:= StrLen(A); LB:= StrLen(B); for I:= 0 to LB-LA do
[for J:= 0 to LA-1 do if A(J) # B(J+I) then J:= LA+1; if J = LA then return B+I; \found ];
return 0; ];
proc ReplaceText(FileName); \replace text in specified file char FileName; char Str(1_000_000), Hello, Bye, Pointer; int Handle, I, C; [Handle:= FOpen(FileName, 0); \get handle for input file FSet(Handle, ^I); \set device 3 input to file handle OpenI(3); \initialize buffer pointers I:= 0; repeat C:= ChIn(3); \read file into memory
Str(I):= C; I:= I+1;
until C = $1A; \EOF FClose(Handle); \release handle
Hello:= "Hello New York!"; \replacement text Bye:= "Goodbye London!"; Pointer:= StrFind(Bye, Str); if Pointer \#0\ then \overwrite (both strings are same length)
for I:= 0 to 15-1 do Pointer(I):= Hello(I);
Handle:= FOpen(FileName, 1); \get handle for output file FSet(Handle, ^O); \set device 3 output to file handle OpenO(3); I:= 0; repeat C:= Str(I); \write file from memory
I:= I+1; ChOut(3, C);
until C = $1A; \EOF Close(3); \flush output buffer FClose(Handle); \release handle ];
int File, I; [File:= ["Alpha.txt", "Beta.txt", "Gamma.txt", "Delta.txt"]; for I:= 0 to 4-1 do ReplaceText(File(I)); ]</lang>
- Programming Tasks
- Solutions by Programming Task
- Ada
- AutoHotkey
- AWK
- BASIC
- BBC BASIC
- C
- C++
- D
- Go
- Haskell
- Icon
- Unicon
- Icon Programming Library
- J
- Java
- Liberty BASIC
- Lua
- OpenEdge/Progress
- Pascal
- Perl
- Perl 6
- PicoLisp
- PowerBASIC
- PureBasic
- Python
- Racket
- REXX
- Ruby
- Run BASIC
- Seed7
- Tcl
- Tcllib
- TUSCRIPT
- TXR
- Vedit macro language
- XPL0
- HTML/Omit
- PARI/GP/Omit
- Text processing
- File handling