How do I write REG_MULTI_SZ values to the Registry? - c++

I'm trying to write a REG_MULTI_SZ value to the Windows Registry using C++. Where I'm stuck is on converting the c-strings that I have to MULTI_SZ format. Is there a convenient way to do this?

You'll have to do it yourself. Given
char ** strings; // array of strings
int N; // number of strings
you count the length of the multi_sz
int len=1;
for(int i=0; i<N; i++)
len += strlen(strings[i])+1;
and fill it
char* multi_sz = malloc(len), ptr=multi_sz;
memset(multi_sz, 0, len);
for(int i=0; i<N; i++) {
strcpy(ptr, strings[i]);
ptr += strlen(strings[i])+1;
}

Here's a C++0x alternative.
static const std::string vals [] =
{
"a", "bb", "ccc"
};
static const size_t num_vals = sizeof(vals)/sizeof(vals[0]);
std::string reg_out = std::accumulate(&vals[0], &vals[num_vals], std::string(), [](std::string& so_far, const std::string& cur) -> std::string
{
so_far += cur;
so_far += '\0';
return so_far;
});
reg_out += '\0';
reg_out.size();
RegSetValueEx(...,...,...,REG_MULTI_SZ, rerinterpret_cast<const BYTE*>(&reg_out[0]), reg_out.size());

If all values are constants, then you may want to use a string literal, rather than unnecessary composing the strings.
If you construct a C++ string from a string literal, then the first null character will terminate it:
string s("aaa\0bbb"); // constructs the string "aaa"
However, a C++ string may contain null characters and can be constructed from a string literal containing null characters as follows:
const char sz[] = "aaa\0bbb";
string szs(sz, sizeof(sz) / sizeof(char)); // constructs the string "aaa\0bbb"
Then you can simply do:
RegSetValueEx(...,...,...,REG_MULTI_SZ, rerinterpret_cast<const BYTE*>(&szs[0]), szs.size());
Note that, unlike what is suggested in other answers, there is no need for two null characters at the end of the value (one for terminating the last string and one for terminating the list). Doing so will in fact add an empty string to the list of values, which may be undesired. In this example, the second string (and the entire list) is terminated by the null character, which is automatically added at the end of the C++ string.

Related

Using toupper for first character in the vector string

How to use toupper() to convert the first letter to uppercase of each element inside a vector of strings?
This is what I've tried...
string word;
vector<string> text;
while(cin >> word){
text.push_back(word);
}
for(decltype(text.size()) i = 0; i != text.size()) {
text[0] = toupper(text);
}
You can simply do this,
string word;
vector<string> text;
while(cin >> word){
word[0] = static_cast<char>(toupper(static_cast<int>(word[0])));;
text.push_back(word);
}
Now you don't have to iterate through the word list to change the first letter of every entry to upper.
for(auto& s : text)
if (!s.empty()) s[0] = std::toupper(s[0]);
Or more fancy:
std::transform(std::istream_iterator<std::string>{std::cin}, {},
std::back_inserter(text),
[](auto s) {
if (!s.empty()) s[0] = std::toupper(s[0]);
return s;
});
There are a couple of problems with your code.
Your 2nd loop runs endlessly, you need to add ++i to the end of the loop to actually iterate each string in the vector and terminate the loop when the end of the vector is reached:
for(decltype(text.size()) i = 0; i != text.size(); ++i)
On a side note, you can replace decltype(text.size()) with vector<string>::size_type instead, or decltype(text)::size_type.
A range-based for loop would avoid both issues:
for(string &str : text)
Regarding your actual error, this line does not compile:
text[0] = toupper(text);
Because toupper() takes a single character as input, but you are passing it the vector itself, rather than a single character from a single string in the vector. You would have needed to do this instead:
text[i][0] = toupper(text[i][0]);
However, you actually need to convert characters to unsigned char when passing them to toupper(), eg (I'm separating each step so you can see it more clearly):
string &str = text[i];
char ch = str[0];
unsigned char uc = static_cast<unsigned char>(ch);
uc = toupper(uc);
ch = static_cast<char>(uc);
str[0] = ch;
text[i] = str;
Which, obviously, can also be written as a string expression, if you want:
text[i][0] = static_cast<char>(
toupper(
static_cast<unsigned char>(
text[i][0]
)
)
);
The reason why the cast is needed is explained on this reference page:
Parameters
ch - character to be converted. **If the value of ch is not representable as unsigned char and does not equal EOF, the behavior is undefined.
...
Like all other functions from <cctype>, the behavior of std::toupper is undefined if the argument's value is neither representable as unsigned char nor equal to EOF. To use these functions safely with plain chars (or signed chars), the argument should first be converted to unsigned char

Cannot use string class to assign characters but works with char array in C++

I'm trying to assign some characters from a string to another string as follows:
string s2;
int ind = -1;
for(int i = 0; i < ch; i++)
{
s2[++ind] = s[i];
}
s2[++ind] = '\0';
Now when I try to print s2 using
cout << s2 << endl;
It prints nothing on the black screen. But when I use char array in place of string declaration for s2, then s2 gets printed successfully. I think both a string and char array's characters can be accessed in the same way. Cannot understand why this happened. Some info about this?
You cannot access the ++indd element of an empty string. Use string's operator += to append a char to the string.
Your code contains Undefined Behavior!
At the beginning your string size is zero and you do not resize it. As a result required memory is not allocated and proper information in string is not updated.
Small string optimization probably prevents a crash which should happen.
Two ways to fix it:
// crappy solution - to match your pattern
string s2;
for(int i=0;i<ch;i++)
{
s2.push_back(s[i]);
}
// a good one
string s2(s, s + strlen(s));

Cannot Convert 'int' to 'const char *'

I'm working with c++ and XE8. Given the following code:
String __fastcall RemoveCharsFromString(String &str, const String &c)
{
for(unsigned int i=0; i<c.Length(); ++i)
{
str.Delete(std::remove(str[0], str.LastChar(), c[i]), str.LastChar());
}
}
Received errors:
Using str.Delete(remove(str[0], str.LastChar(), c[i]), str.LastChar()); results in a
Cannot convert 'int' to 'const char *'
error inside the for loop.
Using str.Delete(std::remove(str[0], str.LastChar(), c[i]), str.LastChar()); results in a
Could not find a match for 'remove(wchar_t,wchar_t*,wchar_t)'
error inside the for loop.
Searching SO and the web, it's my understanding this error is usually received when code is written with single quotes when double quotes should have been used. I don't believe that scenario is applicable in this case.
The return type of String is Embarcadero's UnicodeString. Details can be found here: RAD Studio VCL Reference - UnicodeString Class
In C++Builder, String refers to System::String, which is an alias for System::UnicodeString in XE8.
There are a lot of mistakes in your code.
The System::String::Delete() method expects an index and a count as input, but that is not what you are trying to pass to it. You are expecting Delete() to work like the STL std::wstring::erase() method, and that is simply not the case.
You are not taking into account that System::String::operator[] is 1-based. It is not 0-based, like your code is assuming.
The System::String::LastChar() method returns a pointer to the last UTF-16 encoded Unicode character in the string. It does not return a pointer to the string's null terminator, like your code is assuming.
You are calling the STL std::remove() algorithm that takes a range of iterators as input, shifts all copies of the specified value to the end of the range, and then returns a new iterator to where the "removed" values have been moved to within the range (so they can be erase()'d from the container that owns the iterators). You cannot mix System::String::Delete() and std::remove() the way you are attempting to do. If you really want to use std::replace(), you need to use it more like this instead:
String __fastcall RemoveCharsFromString(String &str, const String &c)
{
for(int i = 1; i <= c.Length(); ++i)
{
const Char *start = str.c_str();
const Char* end = start + str.Length();
Char* ptr = std::replace(start, end, c[i]);
str.Delete(1 + std::distance(start, ptr), std::distance(ptr, end));
}
}
That being said, Embarcadero's RTL has its own System::Sysutils::StringReplace() function that you can use instead of std::replace():
#include <System.SysUtils.hpp>
String __fastcall RemoveCharsFromString(String &str, const String &c)
{
for(int i = 1; i <= c.Length(); ++i)
{
str = StringReplace(str, c[i], L"", TReplaceFlags() << rfReplaceAll);
}
}
Or, if you need to take UTF-16 surrogates into account in the c string (which std::remove() does not account for):
#include <System.SysUtils.hpp>
#include <System.Character.hpp>
String __fastcall RemoveCharsFromString(String &str, const String &c)
{
int i = 1;
while (i <= c.Length())
{
String chr;
if (IsSurrogatePair(c, i))
chr = c.SubString(i, 2);
else
chr = c.SubString(i, 1);
str = StringReplace(str, chr, L"", TReplaceFlags() << rfReplaceAll);
i += chr.Length();
}
}

Delete repeated characters from a random word

I'm making a class to delete repeated character from a random word. For example if the input is "aabbccddeeff", it should output "abcdef". However my output contains strange characters after "abcdef". The main.cpp file already exists as the requirements for creating the class. Please see the following codes:
main.ccp
#include <iostream>
#include "repeatdeletion.h"
using namespace std;
int main()
{
char* noRepeats;
int length;
string s;
cout<<"Enter a random word with repeating characters: ";
cin>>s;
RepeatDeletion d;
length=s.length();
noRepeats=d.deleteRepeats(s, length);
cout<<"Your word without any repeating characters: ";
for (int k=0; k<length; k++){
cout<<noRepeats[k];
}
cout<<endl;
delete [] noRepeats;
noRepeats=NULL;
return 0;
}
repeatdeletion.h
#ifndef REPEATDELETION_H
#define REPEATDELETION_H
#include <iostream>
using namespace std;
class RepeatDeletion
{
char* c;
char arr[128]={};
bool repeated;
bool isRepeated(char);
public:
RepeatDeletion();
~RepeatDeletion();
char* deleteRepeats(string, int);
};
#endif // REPEATDELETION_H
repeatdeletion.cpp
#include "repeatdeletion.h"
RepeatDeletion::RepeatDeletion()
{
repeated=false;
}
RepeatDeletion::~RepeatDeletion()
{
delete [] c;
c=NULL;
}
bool RepeatDeletion::isRepeated(char c){
bool repeated=false;
if (arr[c]>=1){
repeated=true;
arr[c]++;
}else{
arr[c]++;
}
return repeated;
}
char* RepeatDeletion::deleteRepeats(string str, int len){
c=new char[len];
int j=0;
for (int i=0; i<len; i++){
if (isRepeated(str[i])==false){
c[j]=str[i];
j++;
}
}
return c;
}
Your return character array is not null terminated.
The length function of string does not include \0.
You have two choices
Add null at the end of returned character array, and std::cout the char array directly (instead of char by char)
Output the final length of your char array, and use that as range to print it char by char
Your printing loop loops using the old and unmodified string length. That means you will go outside the characters you added to memory returned by deleteRepeats.
The easiest solution to handle this is to terminate the data as a proper string, and check for the terminator in the loop.
If you want to use a C-string array, they have a null terminator at the end. That means you'll want to (in deleteRepeats) define your character array one character larger than the length:
c=new char[len+1];
And, after the for loop, ensure you put that null terminator in:
c[j] = '\0';
Then, in your calling function, you can just do:
cout << noRepeats;
Even if you don't want to use C strings, you'll need to communicate the new length back to the caller somehow (currently, you're using the original length). The easiest way to do that is (IMNSHO) still using a C-style string and using strlen to get the new length (a).
Otherwise, you're going to need something like a reference parameter for the new length, populated by the function and used by the caller.
(a) But I'd suggest rethinking the way you do things. If you want to be a C++ coder, be a C++ coder. In other words, use std::string for strings since it avoids the vast majority of problems people seem to have with C strings.
That's because in your code you write the following:
cout<<"Your word without any repeating characters: ";
for (int k=0; k<length; k++){
cout<<noRepeats[k];
}
cout<<endl;
Here, length refers to the length of the original string (which you, by the way shouldn't pass to your deleteRepeats method). I would suggest you make deleteRepeats return a string and write something like this:
std::string noRepeats = d.deleteRepeats(s);
std::cout << "Your word without any repeating characters: ";
std::cout << noRepeats << std::endl;
C-style string (char *, if you insist) follow the convention that the last character is '\0', indicating that the string ends. You could also change deleteRepeats by appending '\0', i.e.
char* RepeatDeletion::deleteRepeats(string str){
c = new char[str.size() + 1];
int j = 0;
for (int i = 0; i < str.size(); i++){
if(isRepeated(str[i]) == false){
c[j] = str[i];
j++;
}
}
c[j] = '\0';
return c;
}
and in your main
std::cout << noRepeats << std::endl;
instead of the for loop. But really, you should use std::string, and if possible not mix it with char *. Hope that helps.
for(k=0;k<length;k++)
Here length should be the exact length of noRepeats, but not of s
so :
char* RepeatDeletion::deleteRepeats(string str, int len)
should return the length-after too
use std::unique it does what you want:
std::string s{};
std::cin>>s;
auto it = std::unique(std::begin(s), std::end(s));
s.resize(std::distance(std::begin(s),it));
std::cout << s;
the way it works is to go through the range begin to end and move all the remaining elements forward if the current element is equal to the next. It returns the position of the end of the new string (it in this example) but does not actually shorten the string so on the next line we shorten the string to the length equal to the distance of begin() to it.
see live at http://ideone.com/0CeaHW

Environment Variables are in a char* how to get it to a std::string

I am retrieving the environment variables in win32 using GetEnvironmentStrings(). It returns a char*.
I want to search this string(char pointer) for a specific environmental variable (yes I know I can use GetEnvironmentVariable() but I am doing it this way because I also want to print all the environment variables on the console aswell - I am just fiddling around).
So I thought I would convert the char* to an std::string & use find on it (I know I can also use a c_string find function but I am more concerned about trying to copy a char* into a std::string). But the following code seems to not copy all of the char* into the std::string (it makes me think there is a \0 character in the char* but its not actually the end).
char* a = GetEnvironmentStrings();
string b = string(a, sizeof(a));
printf( "%s", b.c_str() ); // prints =::=
Is there a way to copy a char* into a std::string (I know I can use strcpy() to copy a const char* into a string but not a char*).
You do not want to use sizeof() in this context- you can just pass the value into the constructor. char* trivially becomes const char* and you don't want to use strcpy or printf either.
That's for conventional C-strings- however GetEnvironmentStrings() returns a bit of a strange format and you will probably need to insert it manually.
const char* a = GetEnvironmentStrings();
int prev = 0;
std::vector<std::string> env_strings;
for(int i = 0; ; i++) {
if (a[i] == '\0') {
env_strings.push_back(std::string(a + prev, a + i));
prev = i;
if (a[i + 1] == '\0') {
break;
}
}
}
for(int i = 0; i < env_strings.size(); i++) {
std::cout << env_strings[i] << "\n";
}
sizeof(a) in what you have above will return the size of char*, i.e. a pointer (32 or 64bits usually). You were looking for function strlen there. And it's not actually required at all:
std::string b(a);
should be enough to get the first environment variable pair.
The result of GetEnvironmentStrings() points to memory containing all environment strings. Similar to the solution of Puppy it will be put into a vector of string, where each string contains just one environment variable ("key=value")
std::vector<std::string> env_strings;
LPTCH a = GetEnvironmentStrings();
As example we will have 2 environment variables defined:
"A=ABC"
"X=XYZ"
LPTCH a will be:
A=ABC\0X=XYZ\0\0
Each variable is '\0' - terminated and finally the complete environment string (a) will be terminated with an additional '\0'.
strlen will return the size to the first occurrence of the termination character '\0'. The last string will always be empty.
while ((std::size_t len = strlen(a)) > 0)
{
env_strings.push_back(std::string(a, len));
a += len + 1;
}
Multi-byte character
For multi-byte characters it will work as well:
LPTCH a = GetEnvironmentStrings();
std::vector<std::wstring> env_strings;
while ((std::size_t len = wcslen(a)) > 0)
{
env_strings.push_back(std::wstring(a, len));
a += len + 1;
}
FreeEnvironmentStrings(a);
Does the following causes any problems?
char* a = GetEnvironmentStrings();
string b;
b=a;
printf( "%s", b.c_str() );
When you say:
string b = string(a, sizeof(a));
you are getting the size of a, which is a pointer and is probably 4. So you will get the first 4 characters. I'm not sure what you are really trying to do, but you should be able just to say:
string b( a );
char* a = ...;
string str(a);
string b;
b = a;
I assume you mean the Windows API GetEnvironmentStrings function. So, test the result against nullptr and perform simple assignment:
char* env = ::GetEnvironmentStrings();
if (0 != env)
{
std::string senv = env;
// use senv to find variables
}
else
{
// report problem or ignore
}