Globally replace text in several files: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|C}}: incorrect tag)
(→‎{{header|C}}: replaced incorrect code)
Line 14: Line 14:


=={{header|C}}==
=={{header|C}}==
<lang C>#include <stdio.h>
{{incorrect|C|Code will segfaut if new string is considerably longer than old string and memmove() overruns the buffer}}
<lang C>#include<stdio.h>
#include <stdlib.h>
#include<stdlib.h>
#include <stddef.h>
#include<string.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)
char*ri="Goodbye London!",*ro="Hello New York!";
{
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;
void die(char*s){
start++;
perror(s);
}
exit(1);
return 0;
}
}


char*loadFile(char*s){
int replace(char *from, char *to, char *fname)
{
int x;
#define bail(msg) { warn(msg" '%s'", fname); goto done; }
FILE*f=fopen(s,"rb");
struct stat st;
if(!f) die("fopen failed");
int ret = 0;
fseek(f,0,SEEK_END);
char *buf = 0, *start, *end;
s=malloc((x=ftell(f))+1);
size_t len = strlen(from), nlen = strlen(to);
if(!s) die("malloc failed");
int fd = open(fname, O_RDWR);
rewind(f);
fread(s,1,x,f);
s[x]=0;
fclose(f);
return s;
}


if (fd == -1) bail("Can't open");
void saveFile(char*fn,char*s){
if (fstat(fd, &st) == -1) bail("Can't stat");
FILE*f=fopen(fn,"wb");
if(!f) die("fopen");
if (!(buf = malloc(st.st_size))) bail("Can't alloc");
if (read(fd, buf, st.st_size) != st.st_size) bail("Bad read");
fwrite(s,1,strlen(s),f);
fclose(f);
}


start = buf;
void stringReplace(char*s,char*a,char*b){
end = find_match(start, buf + st.st_size, from, len);
char*p;
if (!end) goto done; /* no match found, don't change file */
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);
}
}


ftruncate(fd, 0);
void process(char*f){
lseek(fd, 0, 0);
char*s=loadFile(f);
do {
stringReplace(s,ri,ro);
write(fd, start, end - start); /* write content before match */
saveFile(f,s);
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(int argc,char**argv){
int main()
{
if(argc) while(--argc) process(*++argv);
char *from = "Goodbye, London!";
return 0;
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>
}</lang>



Revision as of 16:05, 2 July 2011

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.

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>

C

<lang C>#include <stdio.h>

  1. include <stdlib.h>
  2. include <stddef.h>
  3. include <string.h>
  4. include <sys/types.h>
  5. include <fcntl.h>
  6. include <sys/stat.h>
  7. include <unistd.h>
  8. include <err.h>
  9. 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) {

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