C++ String Find and Replace

Aug 29, 2011 at 8:44pm
:D Hello everyone,

I am making a console app in MSVS 2008. I was wondering if it is possible to find part of a string and replace it with the following letters:

"128." = "a"
"70." = "b"
"88." = "c"

So if I had "128.88.70.128.70." it would go to "acbab". I got this far from a previous forum with the help of ZED0815:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <string>
#include <Windows.h> 


using namespace std;

int main ()
{    
  string str ("128.88.70.128.70.");
  string searchFora ("128.");    
  string replaceBya ("a");

 string searchForb ("70.");
  string replaceByb ("b");

 string searchForc ("88.");
  string replaceByc ("c");     

  
  size_t found;
  
  
  
  found = str.find( searchFora );
  while (found!=string::npos){
    str.replace( found, sizeof( searchFora ), replaceBya );
    found = str.find( searchFora );
  };
  
    found = str.find( searchForb );
  while (found!=string::npos){
    str.replace( found, sizeof( searchForb ), replaceByb );
    found = str.find( searchForb );
  };
  
    found = str.find( searchForc );
  while (found!=string::npos){
    str.replace( found, sizeof( searchForc ), replaceByc );
    found = str.find( searchForc );
  };

  cout << str << endl;
  
  cin.clear();
cin.ignore(255, '\n');
cin.get();
  
  
  return 0;
}


It output only the first 3 character "acb". Do I have to do something to the size_t found; to get it working?

Thanks for any help,

Muhasaresa
Aug 29, 2011 at 9:02pm
You can not use sizeof() to get the length of a std::string. You must use either std::string::length() or std::string::size()
1
2
3
str.replace( found, sizeof( searchFora ), replaceBya ); // this is wrong

str.replace(found, searchFora.size(), replaceBya); // try this 
Aug 29, 2011 at 11:55pm
Heh heh heh...

Such a function has long existed in Tcl
http://www.tcl.tk/man/tcl8.5/TclCmd/string.htm#M39

Here's my C++ version:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#pragma once
#ifndef DUTHOMHAS_STRING_MAP_HPP
#define DUTHOMHAS_STRING_MAP_HPP

#include <algorithm>
#include <iterator>
#include <string>

template <typename Iterator>
std::string string_map( const std::string& s, Iterator begin, Iterator end )
  {
  std::string result;

  for (size_t n = 0; n < s.length(); )
    {
    for (Iterator key = begin; key != end; std::advance( key, 2 ))
      {
      if (s.find( *key, n ) == n)
        {
        Iterator value = key;
        std::advance( value, 1 );
        result.append( *value );
        n += std::string( *key ).length() - 1;
        goto next;
        }
      }
    result += s[ n ];
    next: ++n;
    }

  return result;
  }

template <typename String, size_t N>
inline
std::string string_map( const std::string& s, String (&mapping)[ N ] )
  {
  return string_map( s, mapping, mapping + N );
  }

template <typename Container>
inline
std::string string_map( const std::string& s, const Container& mapping )
  {
  return string_map( s, mapping.begin(), mapping.end() );
  }

#endif 

This was my first pass at it -- I'm sure I could improve it some. And yes, that goto was intensional -- it demonstrates valid use.

[flame on] Anyone who wants to correct me has to do so without getting flaggy or involving other overhead.

Hint: It can be done easily enough. If no one does I will before long.
Aug 30, 2011 at 12:45am
I'm not flaming. I just saw a challenge. For the record, it makes sense to use goto instead of trying to break awkwardly out of a heavily nested structure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for (size_t n = 0; n < s.length(); ++n)
  {
  result += s[ n ];
  for (Iterator key = begin; key != end; std::advance( key, 2 ))
    {
    if (s.find( *key, n ) == n)
      {
      Iterator value = key;
      std::advance( value, 1 );
      result.replace( result.length() - 1, 1, *value );
      n += std::string( *key ).length() - 1;
      break;
      }
    }
  }


Edit: moved ++n to within the for loop header
Last edited on Aug 30, 2011 at 2:17am
Aug 30, 2011 at 2:45am
I wrote a similar function to replace string patterns from an input stream to an output stream. Its fairly involved because I tried to make it efficient and so it uses a circular buffer to store the matched portion of the input stream. However, I have extracted and converted the string replace portion to work on strings rather than streams. I manage to avoid a goto by putting the key match test in the for loop that tries each key:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <string>
#include <vector>
#include <iostream>

typedef std::pair<std::string, std::string> str_pair;
typedef std::vector<str_pair> str_map;
typedef str_map::iterator map_iter;
typedef str_map::const_iterator map_citer;

std::string map_copy(const str_map& m, const std::string& str)
{
	std::string out;

	map_citer i;
	for(size_t pos = 0; pos < str.size(); ++pos)
	{
		for(i = m.begin(); i != m.end() && str.find(i->first, pos) != pos; ++i);
		if(i != m.end())
		{
			out.append(i->second);
			pos += i->first.length() - 1;
			continue;
		}
		out.append(1, str[pos]);
	}
	return out;
}

int main()
{
	str_map map1;
	str_map map2;

	map1.push_back(str_pair("abc", "1"));
	map1.push_back(str_pair("ab", "2"));
	map1.push_back(str_pair("a", "3"));
	map1.push_back(str_pair("1", "0"));

	map2.push_back(str_pair("1", "0"));
	map2.push_back(str_pair("ab", "2"));
	map2.push_back(str_pair("a", "3"));
	map2.push_back(str_pair("abc", "1"));

	std::cout << map_copy(map1, "1abcaababcabababc") << '\n';
	std::cout << map_copy(map2, "1abcaababcabababc") << '\n';

	return 0;
}
01321221
02c322c222c
Aug 30, 2011 at 11:17am
Thanks everyone for your help :D

Muhasaresa
Topic archived. No new replies allowed.