tags:

views:

1989

answers:

4

I'm trying to find a way to replace all instances of a string token in a file with another string.

How can I do this in C++ with the win32 API?

In other languages this is an easy thing to do, but in C++ I am just lost.

EDIT: For some context, this is for a WiX custom action. So portability is not a main priority, just the most simplest solution.

+4  A: 

If the file fits in memory – it's simpler. Call OpenFile() to open file, GetFileSize() to determine file size, allocate enough memory, call ReadFile() to read file, then CloseFile. Do replacement in memory (use strstr() or similar function), then again OpenFile(), WriteFile(), CloseFile().

If the file is large - create a temporary file and read the source file in chunks and write filtered text to the temporary file, then call DeleteFile() to delete the original file and MoveFile() to move the filtered file.

sharptooth
+3  A: 

You could use the Boost.Regex Library which should resemble most of the functionality you find on other platforms.

It would work like this:

In this example you’ll find how you can replace a string matching a pattern.

#include <boost/regex.hpp>
#include <string>

int main()
{
    boost::regex pattern ("b.lug",boost::regex_constants::icase|boost::regex_constants::perl);
    std::string stringa ("Searching for bolug");
    std::string replace ("BgLug");
    std::string newString;

    newString = boost::regex_replace (stringa, pattern, replace);

    printf("The new string is: |%s|\n",newString.c_str());

    return 0;
}

but you would have of course to add the file reading/writing.

Kosi2801
This is for a msi custom action. External libraries are no-go.
jumpinjackie
In that case it gets a bit more complicated. Maybe you could use C++ streams and create a manipulator which replaces certain strings with others.See http://stackoverflow.com/questions/535444/custom-manipulator-for-c-iostream
Kosi2801
By "external libraries" do you mean DLLs? Because Boost can be statically linked to your executable. Actually, if you use xpressive instead of regex, it is a header-only library. There's no linkage at all.
Ferruccio
+1  A: 

Why do you have to use the Win32 API? It's easy enough using straight C++, I wouldn't confuse the issue by adding artificial constraints. Just open your input file, open an output file, and read a line from your input. While you haven't hit EOF in your input file, use a regex to look for your token. If you find it, then replace it with your text. Write the line to the output file. Read another line from the input. When you get EOF on the input, close it. Be sure any pending output gets flushed from the output buffer. Close the output file. Done.

TMN
+1  A: 

As per sharptooth's solution, I knocked up some C code to do a find and replace on a file. I used stdio calls (strlen, strstr, strcpy and strcat) to do the string manipulation (rather than win32 calls), so your only dependancy is the C run time.

This is certainly not code I would use in a production system. I would use stuff from toolkit string manipulation libraries to make this much cleaner (and not so much with the fixed length buffers). I probably wouldn't use boost, I don't like the overhead. But I figured you might like an example with just the basics (N.B. This writes the altered buffers out to .temp).

#include <stdio.h>

#define BUF_LEN 2048

int findAndReplace (const char * file, const char * find, const char * replace)
{
    int replaceCount = 0;
    FILE * f = fopen (file, "rt");

    if (strstr(replace, find))
     return 0; // replacing blah with stuff_blah_stuff

    unsigned int findLen = strlen (find);
    char tempFile [BUF_LEN];
    strcpy (tempFile, file);
    strcat (tempFile, ".temp");
    FILE * writeF = fopen (tempFile, "wt");

    if (!f || !writeF)
     return 0;

    printf ("Processing %s - %s to %s\n", file, find, replace);

    char lineBuf [BUF_LEN];
    memset (lineBuf, 0, BUF_LEN);
    char tempLineBuf [BUF_LEN];
    memset (tempLineBuf, 0, BUF_LEN);

    // read each line of the file
    while (fgets (lineBuf, BUF_LEN, f))
    {
     // get the position of find in the line buffer
     char * pos = strstr (lineBuf, find);
     while (pos)
     {
      strncpy (tempLineBuf, lineBuf, pos - lineBuf);
      strcat (tempLineBuf, replace);
      strcat (tempLineBuf, pos + findLen);
      replaceCount++;

      // replace the current buf with the replaced buffer
      strncpy (lineBuf, tempLineBuf, BUF_LEN);
      memset (tempLineBuf, 0, BUF_LEN);

      pos = strstr (lineBuf, find);
     }

     printf ("writing new line %s\n", lineBuf);
     fputs (lineBuf, writeF);
    }

    fclose (f);
    fclose (writeF);

    return replaceCount;
}

int main ()
{
    printf ("Made %d replacements\n", findAndReplace ("blah.txt", "marker", "testing_blah"));
}
Cannonade