Skip to main content
GameDev.net gamedev.net
🔒 Locked

Creating single string from array of strings C++

Started by WGS_Stillwater Mar 31, 2020 at 10:24 AM 18 replies 20.9k views
Original Post
WGS_Stillwater
WGS_Stillwater

Answers seem to be all over the place as to the best approach. I just need a simple way to take an array of string values and combine them into one and a way to separate them again to re-insert them into an array. Nothing fancy required.

Basically I just need the proper syntax for joining two strings with some type of separator and the syntax for breaking those strings back up again. I'll handle the looping function outside of the combining function.

SyncViews
SyncViews

I think the thing here is since C++ doesn't offer a built in way to do this, but there are lots of ways to do it your self.

Of special note is that in the general case to join a bunch of strings, and then split them again, you have to consider what if one of the strings actually contains your separator? Or what if the API or other requirements disallow certain elements (e.g. while std::string can contain null (0) elements, many char* API's can't. And if you want to display your string, many more restrictions). Which then brings you into escaping/encoding.

And again many solutions to this, many languages/schemes use a backslash to handle just the “special” characters (including C/C++ source code itself, but the standard lib has no function to create or parse these…), things like base64 will just encode everything, other schemes like percent encoding (URLs), or enclosing with double quotes (e.g. CSV, which then leads to double double quotes). So no real right choice, if you need to exchange data with one (say JavaScript/JSON) use that, otherwise just pick one.

RulerOfNothing
RulerOfNothing

To merge two or more strings, you could use

std::stringstream str(s1);
str << “|” << s2;

where you replace | with whatever you choose to be the separator, and then use str.str() to get the final string. To break the string apart you use

std::getline(str,s1,"|");
std::getline(str,s2,"|");

and so on for however many strings you put in the stringstream. Also to do this you need to include and .

Juliean
Juliean

std::stringstream is a bit on the heavy side of things. I would probably just create an std::string itself, preferably in a helper function (and use std::string_view if you have access to C++17):

constexpr std::string_view SEPARATOR = “|”;

std::string concat(std::string_view a, std::string_view b)
{
    std::string result;
    result.reserve(a.size() + b.size() + SEPARATOR.size());
    
    result += a;
    result += SEPARATOR;
    result += b;
    
    return result
}

std::pair<std::string_view, std::string_view> split(std::string_view string)
{
     const auto pos = string.find(SEPARATOR);
     assert(pos != std::wstring::npos);
              
     return
     {
         string.substr(0, pos),
         string.substr(pos + 1);
     };
}

This would require the least amount of allocations possible. I generally only manipulate strings view string_view for that reason. Only thing to keep in mind is that you have to convert to std::string again if you intend to store the split-result while the concated-string might not exist anymore.

WGS_Stillwater
WGS_Stillwater

Thanks guys. I'm pairing this with blueprints in ue4. I need to store some arrays to send them to a column in my sql db. I'm setting up a custom node for this bit. It occured to me I just need to setup one node technically to separate the strings since I can use append on a foreachloop function to create a single string.

rafaelsantana
rafaelsantana

Sometimes people over analyze things, to join several strings.

string first = “jessica”;
string last = “malessik”;
string joinstrings = first + last; // combine them together

to insert stuff between them?

string joinstrings = first + “ ### " + last; // inserts ### between first and last

you can insert a string into a vector like this

vector<string>mystuff(100);
mystuff.insert(mystuff.begin() + 10,first); // inserts first into position 10

I recommend you read,

http://www.cplusplus.com/reference/string/string/ — for string reference

and

http://www.cplusplus.com/reference/vector/vector/ — for vector reference

you can use other types of arrays as well.

WGS_Stillwater
WGS_Stillwater
How do I tell the function to convert the outputs into FString before returning?
WGS_Stillwater
WGS_Stillwater
and if I try it this way… apparently it's discarding the output and the compiler isn't having it.
Bregma
Bregma

Just a suggestion on tokenizing the string.

#include <iostream>
#include <regex>
#include <string>
#include <vector>

using namespace std;

/*
 * This here is the tokenizer. Splits an input string on “I” characters into an arbitrary number
 * strings appended to the result vector.
 */
void
split_on_sep(string const& src, vector<string>& result)
{
  static const regex  marker{"[^|]+"};
  auto tokens_begin = sregex_iterator{src.begin(), src.end(), marker};
  auto tokens_end = sregex_iterator{};

  for (auto t = tokens_begin; t != tokens_end; ++t)
    result.push_back((*t).str());
}

/*
 * Test it.
 */
int
main()
{
  const  string dbstring{"one|two|three"};
  vector<string> result;
  
  split_on_sep(dbstring, result);

  for (auto const& s: result)
    cout << s << "\n";
}

Here's the output.

$ g++ -std=c++14 -o strs strs.cpp && ./strs
one
two
three
Stephen M. Webb
Professional Free Software Developer
rafaelsantana
rafaelsantana

Bregma said:

Just a suggestion on tokenizing the string.

#include <iostream>
#include <regex>
#include <string>
#include <vector>

using namespace std;

/*
 * This here is the tokenizer. Splits an input string on “I” characters into an arbitrary number
 * strings appended to the result vector.
 */
void
split_on_sep(string const& src, vector<string>& result)
{
  static const regex  marker{"[^|]+"};
  auto tokens_begin = sregex_iterator{src.begin(), src.end(), marker};
  auto tokens_end = sregex_iterator{};

  for (auto t = tokens_begin; t != tokens_end; ++t)
    result.push_back((*t).str());
}

/*
 * Test it.
 */
int
main()
{
  const  string dbstring{"one|two|three"};
  vector<string> result;
  
  split_on_sep(dbstring, result);

  for (auto const& s: result)
    cout << s << "\n";
}

Here's the output.

$ g++ -std=c++14 -o strs strs.cpp && ./strs
one
two
three

All I have to say is, “beautiful ❤️”

WGS_Stillwater
WGS_Stillwater

I think the problem I'm having with any of these functions… the results must end in FString formats.

Juliean
Juliean

You should always be able to construct an FString by using the char-constructor:

std::string stdString;
FString str(stdString.c_str());

This again incurs a whole copy of the strings content, which may or may not be acceptable for your situation. If yo are working with Unreal you are probably better off using the FString-interface for the whole string-manipulation. Check the documentation:

https://docs.unrealengine.com/en-US/API/Runtime/Core/Containers/FString/index.html
There appears to be all the functions to plainly concat and split that string. Only if you want to actually use the regex you most likely have to convert like above.

WGS_Stillwater
WGS_Stillwater

Performance isn't really an issue at this time. I'm a noob when it comes to C++ so that's like 99% of the issue. I'm still working on getting the split function right. I don't need it to break up the entire string. I only need it to split the string at the first delimiter (in this case I'm using “,” for mine) and then output the split and the remaining string. It must output into FString format.

I see FString contains a chop function and a find function, I have to figure out the syntax of how to set it up to do what I'm looking for. Once this is setup I can go back to humming along until the next coding challenge lol.

For instance: WTF?

My understanding is I need to find the delimiter and the do a leftchop and rightchop, and output the results of each respectively.
RulerOfNothing
RulerOfNothing

If the function requires a character as a parameter, you can't pass in a string because strings aren't characters. Also reading the documentation that is apparently not how you are supposed to use FindChar, so you want something like

int pos;
char separator=',';
UE4Str.FindChar(separator,pos);
WGS_Stillwater
WGS_Stillwater
If I do not include declspec(noreturn) the compiler demands a return value (which creates a “return” output on the node which I don't actually want… I just want output of choppedstring and the remainingstring. If I include __declspec(noreturn) in the header on the function it kicks back another error.
WGS_Stillwater
WGS_Stillwater
The function is now doing what I want… other than having a return value. I need to properly remove the return and it's golden for what I need it to do. EDIT: THESOLUTION = changing it from “FString UClass::Function” to “void Uclass::Function” - void does not return. I hope this helps someone else lol. Thanks for all the help everyone!
Juliean
Juliean

__declspec(noreturn) has nothing to do with a return value, but tells the compiler that the function does not reach its return statement. This mostly applies to functions that terminate the program, or are guaranteed to throw an exception. So mostly stuff for low-level library writers to silence warnings and slightly allow the compiler to optimize certain paths of execution. Nothing you should ever need, and you need to be carefull with that because it can cause your program to behave weird (if a function marked noreturn actually returns, its undefined behaviour).

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.