Adding spaces between names in a string

I'm thinking this is a simple problem, but I am having a lot of trouble figuring out.

I need to be able to take a string like "R2R1R2+*R1+" and add spaces to make it look like this "R2 R1 R2 + * R1 +"

I have an array that contains the file names (R1 and R2), so I'm thinking that I can search the string somehow, but this is the best I've been able to come up with:

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
string expFormat(string exp, string fNames[], string expNames[])
{
	int size = exp.length();

	for(int i = 0; i < size; i++)
	{
		if(exp.find(fNames[i]) != string::npos)
		{
			int index = exp.find(fNames[i]);
			int ind;
			exp.insert(index, " ");
			ind = 1 + index + fNames[i].length();
			exp.insert(ind, " ");
		}
		else if (exp.find(expNames[i]) != string::npos)
		{
			int index = exp.find(expNames[i]);
			int ind;
			exp.insert(index, " ");
			ind = 1 + index + expNames[i].length();
			exp.insert(ind, " ");
		}

	}

	int sz = exp.length();

	for(int i = 0; i < sz; i++)
	{
		if(exp[i] == '+')
		{
			cout << "Check: +" << endl;
			exp.insert(i+1, " ");
			exp.insert(i, " ");
		}
		else if (exp[i] == '*')
		{
			cout << "Check: *" << endl;
			exp.insert(i+1, " ");
			exp.insert(i, "  ");
		}
	}

	return exp;
}


The fNames array is another array of file names. I know this is pretty thrown together, but it works for some cases. Just not the one above and some others. I'm going to be putting the result in a stringstream to get rid of the spaces, so it doesn't really matter how many spaces there are between the names, as long as they are in the correct places.

I'd be very appreciative if someone could help me figure out how to get this working.
Last edited on
One way to do it:
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
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <cstdio>

using namespace std;

// need to be able to take a string like "R2R1R2+*R1+" 
// and add spaces to make it look like this "R2 R1 R2 + * R1 +"

string add_spaces(const string& input)
{
  string output;

  for (char ch : input)
  {
    switch(ch)
    {
      case 'R':
      case '+':
      case '*':
      {
        if (!output.empty() && output.back() != ' ')
        {
          output += ' ';
        }
      }
      break;
    }    
    output += ch;
  }
  return output;
}

int main()
{
  string input = "R2R1R2+*R1+";
  string output = add_spaces(input);
  cout << "Input: " << input << "\n";
  cout << "Output: " << output << "\n";
}

Output

Input: R2R1R2+*R1+
Output: R2 R1 R2 + * R1 +


CAUTION: This code is just an example of how things can be done.
It is neither the only way nor the best way.
It is not properly tested and might not be correct.
That looks good, but the problem is that the file names may not always be the same. They may be named A, B, C, R1, R2, File1, or File2. The names also probably won't be a set length.

And I forgot to mention that the code I posted above outputs something like this: R2 R1 R2 + *R1+
Last edited on
Do you have a way to distinguish whether "AB" means a single file "AB" or 2 files "A" and "B"?

If, for instance, each file name always has exactly 1 capital letter and it is always the first character, then search for capital letters. All subsequent letters that are not capitals, white space nor operators are part of a file name.

If capital letters can be anywhere or nowhere in the file name, then you cannot parse out the file names without some sort of separator in the string.

If valid file names can have operators in them, you have the same problem.

You must have a way to parse a string and determine what is part of a file name, where a file name ends, and what is an operator. For robustness, you should also be able to reject characters that are neither part of a file name nor an operator.
This will only work if names are fully distinguishable: e.g. file1 and file10 will fail (probably).
Compound operators like += will also fail.

It's double-spaced in places, as I'm assuming that all you want to do is separate entities to stringstream them.

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
#include <iostream>
#include <string>
#include <vector>
using namespace std;

string addSpaces( string text, vector<string> allStrings )
{
   if ( text == "" ) return text;             // Trivial case

   for ( string s : allStrings )              // Put a space before and after any specified string
   {
      int pos = 0;                     
      while( true )
      {
         pos = text.find( s, pos );
         if ( pos == string::npos ) break;
         if ( pos != 0 )                      // Don't want a space at the start
         {
            text.insert( pos, " " );   pos++;
         }
         pos += s.size();
         if ( pos >= text.size() ) break;     // Don't want a space at the end
         text.insert( pos, " " );   pos++;    // Comment out this line if you don't want double-spacing
      }
   }
   return text;
}


int main()
{
   vector<string> fileNames{ "File1", "File2", "File12" };
   vector<string> expNames{ "A", "B", "C", "R1", "R2" };
   vector<string> opNames{ "+", "-", "*", "/", "="  };

   // allStrings will contain anything that you need a space preceding
   vector<string> allStrings = fileNames;
   for ( string s : expNames ) allStrings.push_back( s );
   for ( string s : opNames  ) allStrings.push_back( s );

   string test;
   test = "R2R1R2+*R1+";   cout << "\nTest: " << test << "\nResult: " << addSpaces( test, allStrings ) << endl;
   test = "ABCR1R2+ABC";   cout << "\nTest: " << test << "\nResult: " << addSpaces( test, allStrings ) << endl;
   test = "A+BunknownC";   cout << "\nTest: " << test << "\nResult: " << addSpaces( test, allStrings ) << endl;
   test = "File1+R1ACC";   cout << "\nTest: " << test << "\nResult: " << addSpaces( test, allStrings ) << endl;
}


Test: R2R1R2+*R1+
Result: R2  R1  R2  +  *  R1  +

Test: ABCR1R2+ABC
Result: A  B  C  R1  R2  +  A  B  C

Test: A+BunknownC
Result: A  +  B unknown C

Test: File1+R1ACC
Result: File1  +  R1  A  C  C


Last edited on
@doug4
The only way I have to distinguish the file names is an array that contains existing file names. If "AB" is not an existing file name, then I should probably output it as an error, because it does not exist.
Also, all the file names will be alphanumeric. They won't have any special characters in them. But they also could have a name of any length, they will not necessarily be only 1 character long.

@lastchance
I will give that a try...
@lastchance
when I try to do a for loop like
for ( string s : expNames )
it gives me an error like: "range-based ‘for’ loops only available with -std=c++11 or -std=gnu++11"
it gives me an error like: "range-based ‘for’ loops only available with -std=c++11 or -std=gnu++11"


So use -std=c++11 as a compiler option (or update to the latest compiler so that it assumes this by default).

It should run (without any need to change the compiler defaults) in c++ shell.
Last edited on
I should probably do that too, but I got it running with normal loops
here is what I have:

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
string addSpaces(string text, vector<string> allStrings)
{
	   for (int i = 0; i < (j+l+2); i++)              // Put a space before and after any specified string
	   {
	      int pos = 0;
	      while( true )
	      {
	         pos = text.find( allStrings[i], pos );

	         if ( pos == string::npos )
	        	 break;

	         if ( pos != 0 )                      // Don't want a space at the start
	         {
	            text.insert( pos, " " );
	            pos++;
	         }

	         pos += (allStrings[i]).size();

	         if ( pos >= text.size() )
	        	 break;     // Don't want a space at the end

	         text.insert( pos, " " );
	         pos++;    // Comment out this line if you don't want double-spacing
	      }
	   }
	   return text;
}


I'm going to go try to put it where I need it in my program and see if it works like I need it.
I'm not sure how your routine will know what j and l are. You could just use allStrings.size() instead of j+l+2 on your line 3.
I made j and l global variables so that all my functions can have access to them. Maybe not the best idea, but it worked. I will use allStrings.size() next time. I didn't realize you could do that because I've never used vectors before.

Thank you to everyone who replied, and thank you to lastchance for the excellent solution!
Topic archived. No new replies allowed.