Globally replace text in several files: Difference between revisions
(Go solution) |
(→{{header|Pascal}}: add example) |
||
Line 325: | Line 325: | ||
"Hello New York!" |
"Hello New York!" |
||
).</lang> |
).</lang> |
||
=={{header|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> |
|||
=={{header|Perl}}== |
=={{header|Perl}}== |
Revision as of 00:46, 30 October 2011
You are encouraged to solve this task according to the task description, using any language you may know.
The task is to replace every occuring 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.
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>
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>
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(char *buf, char * buf_end, 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 start; start++; } return 0; }
int replace(char *from, char *to, 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() { char *from = "Goodbye, London!"; char *to = "Hello, New York!"; 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" "fmt" "io/ioutil" "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 { gRepFile(oldb, newb, fn) }
}
func gRepFile(oldb, newb []byte, fn string) {
fi, err := os.Stat(fn) if err != nil { fmt.Println(err) return } b, err := ioutil.ReadFile(fn) if err != nil { fmt.Println(err) return } r := bytes.Replace(b, oldb, newb, -1) err = ioutil.WriteFile(fn, r, fi.Permission()) if err != nil { fmt.Println(err) }
}</lang>
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>
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>
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 is not supported on Windows 8.3 operating systems).
<lang python>import fileinput
for line in fileinput.input(inplace=True):
print(line.replace('Goodbye London!', 'Hello New York!'), end=)
</lang>
Ruby
Like Perl:
ruby -pi -e "gsub('Goodbye London!', 'Hello New York!')" a.txt b.txt c.txt
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