I have a string like this: DialogTitle = IDD_SETTING_DLG in a save file (i have already stored it in an array called m_TextArray).
Now i want to get the "IDD_SETTING_DLG" part(or at least " IDD_SETTING_DLG") and store it in a CString variable. I used the Tokenize method but it didn't work.
Here are my codes:
BOOL CTab1::OnInitDialog()
{
UpdateData();
ReadSaveFile();
SetTabDescription();
UpdateData(FALSE);
return TRUE;
}
void CTab1::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_SHOWDES, m_ShowDes);
}
void CTab1::ReadSaveFile()
{
if (!SaveFile.Open(SFLocation, CFile::modeRead | CFile::shareDenyWrite, &ex))
{
ReadSettingFile();
}
else
{
for (int i = 0; i < 100; i++)
{
SaveFile.ReadString(ReadLine);
m_TextArray[i] = ReadLine.GetString();
}
}
}
void CTab1::SetTabDescription() //m_TextArray[2] is where i stored the text
{
Position = 0;
Seperator = _T("=");
m_ShowDes = m_TextArray[2].Tokenize(Seperator, Position);
while (!m_ShowDes.IsEmpty())
{
// get the next token
m_ShowDes = m_TextArray[2].Tokenize(Seperator, Position);
}
}
Anyone solution or hint would be very appreciated.
Since you're merely looking for the part of the string that occurs after a token, there's no need to use Tokenize. Just find the position of the token character (your "=") and get everything after that:
void CTab1::SetTabDescription() //m_TextArray[2] is where i stored the text
{
CString separator = _T("=");
CString source = m_TextArray[2];
// Get position of token...
int position = source.Find(separator);
// If token is found...
if (position > -1 && source.GetLength() > position)
m_ShowDes = source.Mid(position + 1); // extract everything after token
}
Related
I am using Embarcadero C++ Builder.
I have a function that declares a TStringList, uses it throughout the function, then deletes the object at the end of the function.
I have been using this code happily as a 32 bit application and have converted it to a 64 bit application, and now I get an "Invalid Pointer Operation" exception when trying to delete the TStringList. Any ideas?
The odd thing is I had the same trouble with another function that uses a character pointer (using new to create heap memory space) and a delete operation. I ended up creating a local buffer with stack space for that function, but I'm stuck with this one since I would like to use the TStringList object.
Here is the code:
String ReadUserConfig(String ConfigString) {
String UserConfigPath = AppDrive + "\\DC\\userconfig.csv";
TStringList *List = new TStringList;
if (FileExists(UserConfigPath)) { // file present, parse it
try {
List->LoadFromFile(UserConfigPath);
delete List;
}
catch(...) {
ShowMessage("Exception in ReadUserConfig()");
return ReturnString;
}
for (int i = 0; i < List->Count; ++i) {
String thisLine = List->Strings[i];
/* search for ConfigString in this line */
if ((thisLine.Pos(ConfigString) != 0) &&
(thisLine.Pos("USER_CONFIG") != 0)) {
/* grab everything right of ConfigString */
thisLine = thisLine.SubString
(thisLine.Pos(ConfigString) + ConfigString.Length() + 1,
thisLine.Length());
ReturnString = thisLine.Trim();
i = List->Count;
}
}
}
delete List; /* CAUSES INVALID POINTER EXCEPTION */
return ReturnString;
}
As stated in comments, your code has logic bugs in it, causing you to delete the List twice, or leak it completely.
Try something more like this instead:
String ReadUserConfig(String ConfigString) {
String UserConfigPath = AppDrive + "\\DC\\userconfig.csv";
try {
TStringList *List = new TStringList;
try {
if (FileExists(UserConfigPath)) { // file present, parse it
List->LoadFromFile(UserConfigPath);
for (int i = 0; i < List->Count; ++i) {
String thisLine = List->Strings[i];
/* search for ConfigString in this line */
if ((thisLine.Pos(ConfigString) != 0) &&
(thisLine.Pos("USER_CONFIG") != 0)) {
/* grab everything right of ConfigString */
thisLine = thisLine.SubString(thisLine.Pos(ConfigString) + ConfigString.Length() + 1, thisLine.Length());
ReturnString = thisLine.Trim();
break;
}
}
}
}
__finally {
delete List;
}
}
catch(const Exception &e) {
ShowMessage("Exception in ReadUserConfig()\n" + e.Message);
}
catch(...) {
ShowMessage("Exception in ReadUserConfig()");
}
return ReturnString;
}
Or, use a std::auto_ptr (pre C++11) or std::unique_ptr (C++11 and later) instead of a try/finally block:
#include <memory>
String ReadUserConfig(String ConfigString) {
String UserConfigPath = AppDrive + "\\DC\\userconfig.csv";
try {
//std::auto_ptr<TStringList> List(new TStringList);
std::unique_ptr<TStringList> List(new TStringList);
if (FileExists(UserConfigPath)) { // file present, parse it
List->LoadFromFile(UserConfigPath);
for (int i = 0; i < List->Count; ++i) {
String thisLine = List->Strings[i];
/* search for ConfigString in this line */
if ((thisLine.Pos(ConfigString) != 0) &&
(thisLine.Pos("USER_CONFIG") != 0)) {
/* grab everything right of ConfigString */
thisLine = thisLine.SubString(thisLine.Pos(ConfigString) + ConfigString.Length() + 1, thisLine.Length());
ReturnString = thisLine.Trim();
break;
}
}
}
}
catch(const Exception &e) {
ShowMessage("Exception in ReadUserConfig()\n" + e.Message);
}
catch(...) {
ShowMessage("Exception in ReadUserConfig()");
}
return ReturnString;
}
So my task is to fill out my function to work with a test driver that feeds it a random string during every run. For this function I have to convert the first character of every word to a capital and everything else must be lower.
It mostly works but the issue i'm having with my code is that it won't capitalize the very first character and if there is a period before the word like:
.word
The 'w' in this case would remain lower.
Here is my source:
void camelCase(char line[])
{
int index = 0;
bool lineStart = true;
for (index;line[index]!='\0';index++)
{
if (lineStart)
{
line[index] = toupper(line[index]);
lineStart = false;
}
if (line[index] == ' ')
{
if (ispunct(line[index]))
{
index++;
line[index] = toupper(line[index]);
}
else
{
index++;
line[index] = toupper(line[index]);
}
}else
line[index] = tolower(line[index]);
}
lineStart = false;
}
Here's a solution that should work and is a bit less complicated in my opinion:
#include <iostream>
#include <cctype>
void camelCase(char line[]) {
bool active = true;
for(int i = 0; line[i] != '\0'; i++) {
if(std::isalpha(line[i])) {
if(active) {
line[i] = std::toupper(line[i]);
active = false;
} else {
line[i] = std::tolower(line[i]);
}
} else if(line[i] == ' ') {
active = true;
}
}
}
int main() {
char arr[] = "hELLO, wORLD!"; // Hello, World!
camelCase(arr);
std::cout << arr << '\n';
}
The variable active tracks whether the next letter should be transformed to an uppercase letter. As soon as we have transformed a letter to uppercase form, active becomes false and the program starts to transform letters into lowercase form. If there's a space, active is set to true and the whole process starts again.
Solution using std::string
void toCamelCase(std::string & s)
{
char previous = ' ';
auto f = [&](char current){
char result = (std::isblank(previous) && std::isalpha(current)) ? std::toupper(current) : std::tolower(current);
previous = current;
return result;
};
std::transform(s.begin(),s.end(),s.begin(),f);
}
I have a delete function that is supposed to delete a string in an array by writing over it with the previous strings.
The look function see's that Overide matches and should be deleted. But the code i wrote for the loop in Delete is not removing that first spot in the array that Overide has taken up, and the output remains unchanged.
Also each phrase after + is being added into the array so four spots are taken in the array, and sorry i could not make that part look better the formatting screwed it up.
int AR::Look(const std::string & word)
{
int result = -1;
for(int i=0; i<counter; ++i)
{
if( con[i].find(word) != std::string::npos)
result = i;
}
return result;
}
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout<<"word not found\n";
}
else
{
for(int i=0; i<counter-1,i++;)
{
con[i]= con[i+1];
}
}
}
AR their
Ar(1);
theirAr + "Overload the +" + " operator as a member function " + "with chaining to add a string " + "to an Arrary object.";
cout<<theirAr<<endl<<endl;
cout<<"testing Delete and Look. <<endl;
theirAr.Delete("XXXXXX");
theirAr.Delete("Overload");
cout<<"Output after Delete and Look called\n";
cout<<theirArray<<endl<<endl;
You are locating the String but only use the value to write an error if it does not appear; if you find the string at pos N you will delete the first string anyway:
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout<<"word not found\n";
}
else
{
for(int i=0;i<counter-1,i++;) <--- Why don't you use loc here???
{
con[i]= con[i+1];
}
}
}
Also, your Look method would be better returning after the first match:
for ... {
if( con[i].find(word) != std::string::npos)
return i;
}
return -1;
Not sure if this is your problem, but shouldn't this be like so?
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout<<"word not found\n";
}
else
{
for(int i=loc;i<counter-1,i++;) // changes in this line
{
con[i]= con[i+1];
}
}
}
Start at where you found the string and start shuffling them backwards. Also, what shortens the array? i.e. drops the last element off. Looks like that is missing too.
Try this instead:
int AR::Look(const std::string & word)
{
for (int i = 0; i < counter; ++i)
{
if (con[i].find(word) != std::string::npos)
return i;
}
return -1;
}
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout << "word not found" << endl;
}
else
{
for (int i = loc+1; i < counter; ++i)
{
con[i-1] = con[i];
}
--counter;
}
}
Suppose I have this kind of string format:
"<RGB:255,0,0>this text is colored RED.<RGB:0,255,0> While this text is colored GREEN";
I want to extract the values inside the <RGB> i.e 255,0,0 and put it on other variables then delete the chars from '<' to '>'.
My code so far:
//this function is called after the loop that checks for the existence of '<'
void RGB_ExtractAndDelete(std::string& RGBformat, int index, RGB& rgb)
{
int i = index + 5; //we are now next to character ':'
std::string value;
int toNumber;
while (RGBformat[i] != ',')
{
value += RGBformat[i++];
}
++i;
std::stringstream(value) >> toNumber;
rgb.R = toNumber;
value = "";
while (RGBformat[i] != ',')
{
value += RGBformat[i++];
}
++i;
std::stringstream(value) >> toNumber;
value = "";
rgb.G = toNumber;
while (RGBformat[i] != '>')
{
value += RGBformat[i++];
}
++i;
std::stringstream(value) >> toNumber;
value = "";
rgb.B = toNumber;
//I got the right result here which is
//start: <, end: >
printf("start: %c, end: %c\n", RGBformat[index], RGBformat[i]);
//but fail in this one
//this one should erase from '<' until it finds '>'
RGBformat.erase(index, i);
}
If I put the <RGB:?,?,?> on the start of the string, it works but it fails when it finds it next to a non '<' character. Or can you suggest much better approach how to do this?
Use std::str::find to locate the <RGB, :, , and >.
Use std::str::substr to "cut out" the string.
Add if (!std::strinstream(value)>> toNumber) ... to check that the number was actually accepted.
Something like this:
std::string::size_type index = std::RGBformat.find("<RGB");
if (index == std::string::npos)
{
... no "<RGB" found
}
std::string::size_type endAngle = std::RGBformat::find(">", index);
if (endAngle == std::string::npos)
{
... no ">" found...
}
std::string::size_type comma = std::RGBformat.find(",", index);
if (comma == std::string::npos && comma < endAngle)
{
... no "," found ...
}
std::string value = RGBformat.substr(index, comma-index-1);
std::stringstream(value) >> toNumber;
value = "";
rgb.R = toNumber;
std::string::size_type comma2 = std::RGBformat.find(",", comma+1);
if (comma2 == std::string::npos && comma2 < endAngle)
...
Note that this may look a bit clumsier than your current code, but it has the advantage of being a lot safer. If someone passed in "<RGB:55> .... " to your existing code, it would break, because it just keeps going until either you get bored and press a key to stop it, or it crashes, whichever comes first...
If you can use Boost or C++11, this is really the perfect place for regular expressions.
You can match your color specifiers with "\\<RGB:(\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\>" - or if you have C++11 raw string literals, you can write this more readably as R"rx(\<RGB:(\d{1,3}),(\d{1,3}),(\d{1,3})\>)rx".
Parse it with
std::getline
http://en.cppreference.com/w/cpp/string/basic_string/getline
This function accepts a delimiter (e.g. '<' or '>') as third argument.
For an example look at:
Basic C++ program, getline()/parsing a file
Here a modified code I use to extract text from html and retrieve data from html tag when I can't use regexp. Otherwise I advice you to use regular expressions they are much more easier to setup.
In my code I ended my tags with "</>" for the color "<RGB:255,0,0>My text</>".
Hope it would help!
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
using namespace std;
typedef struct{
string text;
uint8_t r;
uint8_t g;
uint8_t b;
}TextInfo;
vector<TextInfo> vect;
const vector<TextInfo> & extractInfos(const string & str){
string newStr = str;
vect.clear();
do{
TextInfo info;
int index = newStr.find('>');
if(index != -1 && newStr.find('<') == 0){
// We get "<RGB:r,g,b>"
string color = newStr.substr(0,index+1);
// We extract red color
string red = color.substr(color.find(':') + 1, color.find(',') - color.find(':') - 1);
// We get "g,b>"
color = color.substr(color.find(',') + 1, color.length() - color.find(','));
// We extract green color
string green = color.substr(0,color.find(','));
// We extract "b>"
color = color.substr(color.find(',') + 1, color.length() - color.find('>'));
// We get blue color;
string blue = color.substr(0,color.find('>'));
// string to int into a uint8_t
info.r = atoi(red.c_str());
info.g = atoi(green.c_str());
info.b = atoi(blue.c_str());
// We remove the "<RGB:r,g,b>" part from the string
newStr = newStr.substr(index+1,newStr.length()-index);
index = newStr.find("</>");
// We get the text associated to the color just extracted
info.text = newStr.substr(0,index);
// We remove the "</>" that ends the color
newStr = newStr.substr(index+3,newStr.length()-(index+2));
}else{
// We extract the string to the next '<' or to the end if no other color is set
int i = newStr.find('<');
if(i == -1){
i=newStr.length();
}
info.text = newStr.substr(0,i);
info.r = 0;
info.g = 0;
info.b = 0; // No color then we put default to black
// We get the new part of the string without the one we just exctacted
newStr = newStr.substr(i, newStr.length() - i);
}
// We put the data into a vector
vect.push_back(info);
}while(newStr.length() != 0); // We do it while there is something to extract
return vect;
}
int main(void){
vector<TextInfo> myInfos = extractInfos("<RGB:255,0,0>String to red</><RGB:0,255,0>Green string</>Default color string");
for(vector<TextInfo>::iterator itr = myInfos.begin();itr != myInfos.end();itr++){
cout << (*itr).text << endl;
}
return 0;
}
I'm interested in unescaping text for example: \ maps to \ in C. Does anyone know of a good library?
As reference the Wikipedia List of XML and HTML Character Entity References.
For another open source reference in C to decoding these HTML entities you can check out the command line utility uni2ascii/ascii2uni. The relevant files are enttbl.{c,h} for entity lookup and putu8.c which down converts from UTF32 to UTF8.
uni2ascii
I wrote my own unescape code; very simplified, but does the job: pn_util.c
Function Description: Convert special HTML entities back to characters.
Need to do some modifications to fit your requirement.
char* HtmlSpecialChars_Decode(char* encodedHtmlSpecialEntities)
{
int encodedLen = 0;
int escapeArrayLen = 0;
static char decodedHtmlSpecialChars[TITLE_SIZE];
char innerHtmlSpecialEntities[MAX_CONFIG_ITEM_SIZE];
/* This mapping table can be extended if necessary. */
static const struct {
const char* encodedEntity;
const char decodedChar;
} entityToChars[] = {
{"<", '<'},
{">", '>'},
{"&", '&'},
{""", '"'},
{"'", '\''},
};
if(strchr(encodedHtmlSpecialEntities, '&') == NULL)
return encodedHtmlSpecialEntities;
memset(decodedHtmlSpecialChars, '\0', TITLE_SIZE);
memset(innerHtmlSpecialEntities, '\0', MAX_CONFIG_ITEM_SIZE);
escapeArrayLen = sizeof(entityToChars) / sizeof(entityToChars[0]);
strcpy(innerHtmlSpecialEntities, encodedHtmlSpecialEntities);
encodedLen = strlen(innerHtmlSpecialEntities);
for(int i = 0; i < encodedLen; i++)
{
if(innerHtmlSpecialEntities[i] == '&')
{
/* Potential encode char. */
char * tempEntities = innerHtmlSpecialEntities + i;
for(int j = 0; j < escapeArrayLen; j++)
{
if(strncmp(tempEntities, entityToChars[j].encodedEntity, strlen(entityToChars[j].encodedEntity)) == 0)
{
int index = 0;
strncat(decodedHtmlSpecialChars, innerHtmlSpecialEntities, i);
index = strlen(decodedHtmlSpecialChars);
decodedHtmlSpecialChars[index] = entityToChars[j].decodedChar;
if(strlen(tempEntities) > strlen(entityToChars[j].encodedEntity))
{
/* Not to the end, continue */
char temp[MAX_CONFIG_ITEM_SIZE] = {'\0'};
strcpy(temp, tempEntities + strlen(entityToChars[j].encodedEntity));
memset(innerHtmlSpecialEntities, '\0', MAX_CONFIG_ITEM_SIZE);
strcpy(innerHtmlSpecialEntities, temp);
encodedLen = strlen(innerHtmlSpecialEntities);
i = -1;
}
else
encodedLen = 0;
break;
}
}
}
}
if(encodedLen != 0)
strcat(decodedHtmlSpecialChars, innerHtmlSpecialEntities);
return decodedHtmlSpecialChars;
}
QString UNESC(const QString &txt) {
QStringList bld;
static QChar AMP = '&', SCL = ';';
static QMap<QString, QString> dec = {
{"<", "<"}, {">", ">"}
, {"&", "&"}, {""", R"(")"}, {"'", "'"} };
if(!txt.contains(AMP)) { return txt; }
int bgn = 0, pos = 0;
while((pos = txt.indexOf(AMP, pos)) != -1) {
int end = txt.indexOf(SCL, pos)+1;
QString val = dec[txt.mid(pos, end - pos)];
bld << txt.mid(bgn, pos - bgn);
if(val.isEmpty()) {
end = txt.indexOf(AMP, pos+1);
bld << txt.mid(pos, end - pos);
} else {
bld << val;
}// else // if(val.isEmpty())
bgn = end; pos = end;
}// while((pos = txt.indexOf(AMP, pos)) != -1)
return bld.join(QString());
}// UNESC