First, as others have noted, do not use getchar()/putchar() or even any of the FILE-based methods such as fopen()/fread()/fwrite() . Use open()/read()/write() instead.
If the file is already uncompressed on disk, do not use channels. If it is compressed, you want to use a pipe to delete the entire read / write cycle. If you unzip the disk back to disk, replace the NUL characters, the data path is disk-> memory / cpu-> disk-> memory / cpu-> disk. If you use a channel, the path is disk-> memory / cpu-> disk. If you are limited on disk, this extra read / write cycle will be about twice as long as it takes to process your gigabytes (or more) of data.
Another thing - given your I / O pattern and the amount of data that you are moving - read the entire file with several GB, write the entire file - the page cache only bothers you. Use direct IO this way in C on Linux (for clarity, headers and reliable error checking):
#define CHUNK_SIZE ( 1024UL * 1024UL * 4UL ) #define NEW_CHAR '@' int main( int argc, char **argv ) { char *buf = valloc( CHUNK_SIZE ); int in = open( argv[ 1 ], O_RDONLY | O_DIRECT ); int out = open( argv[ 2 ], O_WRONLY | O_CREAT | O_TRUNC | O_DIRECT, 0644 ); for ( ;; ) { ssize_t bytes = read( in, buf, CHUNK_SIZE ); if ( bytes < 0 ) { if ( errno == EINTR ) { continue; } break; } else if ( bytes == 0 ) { break; } for ( int ii = 0; ii < bytes; ii++ ) { if ( !buf[ ii ] ) { buf[ ii ] = NEW_CHAR; } } write( out, buf, bytes ); } close( in ); close( out ); return( 0 ); }
Maximize compiler optimization. To use this code for real data, you need to check the results of calling write() - direct input-output in Linux - a real ingenious beast. I had to close the file opened with O_DIRECT and reopen it without direct I / O to write the last bytes of the file in Linux when the last bits were not multiples of the full page.
If you want to go even faster, you can multithreadedly process a process - one stream reads, one stream translates characters, and the other stream writes. Use as many buffers, passing them from stream to stream, if necessary, to support the slowest part of the process at any time.
If you are really interested in learning how fast you can move data, multithreading read / write too. And if your file system supports it, use asynchronous read / write.