Globally replace text in several files

Revision as of 18:23, 17 June 2011 by rosettacode>Markhobley (Category:Text processing)

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.

Task
Globally replace text in several files
You are encouraged to solve this task according to the task description, using any language you may know.

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>

C

<lang C>#include<stdio.h>

  1. include<stdlib.h>
  2. include<string.h>

char*ri="Goodbye London!",*ro="Hello New York!";

void die(char*s){

perror(s);
exit(1);

}

char*loadFile(char*s){

int x;
FILE*f=fopen(s,"rb");
if(!f) die("fopen failed");
fseek(f,0,SEEK_END);
s=malloc((x=ftell(f))+1);
if(!s) die("malloc failed");
rewind(f);
fread(s,1,x,f);
s[x]=0;
fclose(f);
return s;

}

void saveFile(char*fn,char*s){

FILE*f=fopen(fn,"wb");
if(!f) die("fopen");
fwrite(s,1,strlen(s),f);
fclose(f);

}

void stringReplace(char*s,char*a,char*b){

char*p;
int sl=strlen(s)+1,bl=strlen(b);
int oa=0,ob=0;
signed d=strlen(a)-bl;
if(d<0) oa=-d; else ob=d;
while((p=strstr(s,a))!=0){
 memmove(p+oa,p+ob,sl-(p-s)-d);
 memcpy(p,b,bl);
}

}

void process(char*f){

char*s=loadFile(f);
stringReplace(s,ri,ro);
saveFile(f,s);

}

int main(int argc,char**argv){

if(argc) while(--argc) process(*++argv);
return 0;

}</lang>

C++

<lang c++>#include <fstream>

  1. include <iterator>
  2. include <boost/regex.hpp>
  3. include <string>
  4. 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

Works with: D version 2

<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>

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>

strings.icn provides replace.

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>

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>

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>

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

Library: Tcllib (Package: fileutil)

<lang tcl>package require Tcl 8.5 package require fileutil

  1. Parameters to the replacement

set from "Goodbye London!" set to "Hello New York!"

  1. Which files to replace

set fileList [list a.txt b.txt c.txt]

  1. Make a command fragment that performs the replacement on a supplied string

set replacementCmd [list string map [list $from $to]]

  1. 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>