what I am doing is writing a .js file with c++ and it will be used in a .html file to draw an organizational chart.
I am actually making an abstract syntax tree. for this, I write the tree nodes in a js file like the following
var nodes = [
"int main(){",
"String str = "hello 'Jane'";",
"}"
]
there is a problem with the quotes. How do I get the following output with cpp.
var nodes = [
"int main(){",
"String str = \"hello \'Jane\'\";",
"}"
]
for read and write json file you can use boost or QT, I have usually used QT.
for example:
QJson json;
//write jeson
json["test1"] = "hello";
json["test2"] = "jan";
//read from json file
cout<<json["test1"];
cout<<json["test2"];
Write a function called "escape".
int escape(char *out, size_t sz, const char *in)
{
int i = 0;
int j = 0;
for(i=0;in[i];i++)
{
if(j > sz - 2)
/* output buffer too small */
switch(in[i])
{
case '\n'; out[j++] = '\\'; out[j++] = 'n'; break;
case '\\'; out[j++] = '\\'; out[j++] = '\\'; break;
...
default: out[j++] = in[i]; break;
}
}
out[j++] =0;
return j;
}
Related
My intended program is simple: Take each word of a text file and replace it with asterisks if it's a swear word. For instance, if the text file was "Hello world, bitch" then it would be modified to "Hello world, *****".
I have the tool for taking a word as a string and replacing it with asterisks if needed. I need help setting up the main part of my program because I get confused with all the fstream stuff. Should I instead make a new file with the replaced words and then overwrite the previous file?
#include <iostream>
#include <string>
#include <fstream>
const char* BANNED_WORDS[] = {"fuck", "shit", "bitch", "ass", "damn"};
void filter_word(std::string&);
void to_lower_case(std::string&);
int main (int argc, char* const argv[]) {
return 0;
}
void filter_word(std::string& word) {
std::string wordCopy = word;
to_lower_case(wordCopy);
for (int k = 0; k < sizeof(BANNED_WORDS)/sizeof(const char*); ++k)
if (wordCopy == BANNED_WORDS[k])
word.replace(word.begin(), word.end(), word.size(), '*');
}
void to_lower_case(std::string& word) {
for (std::string::iterator it = word.begin(); it != word.end(); ++it) {
switch (*it) {
case 'A': *it = 'a';
case 'B': *it = 'b';
case 'C': *it = 'c';
case 'D': *it = 'd';
case 'E': *it = 'e';
case 'F': *it = 'f';
case 'G': *it = 'g';
case 'H': *it = 'h';
case 'I': *it = 'i';
case 'J': *it = 'j';
case 'K': *it = 'k';
case 'L': *it = 'l';
case 'M': *it = 'm';
case 'N': *it = 'n';
case 'O': *it = 'o';
case 'P': *it = 'p';
case 'Q': *it = 'q';
case 'R': *it = 'r';
case 'S': *it = 's';
case 'T': *it = 't';
case 'U': *it = 'u';
case 'V': *it = 'v';
case 'W': *it = 'w';
case 'X': *it = 'x';
case 'Y': *it = 'y';
case 'Z': *it = 'z';
}
}
}
The usual solution to modifying a file is to generate a new
file, then delete the old and rename the new. In your case,
because your replacement text has exactly the same length as
your new text, you can do it in place, with something like:
std::fstream file( fileName, ios_base::in | ios_base::out );
if ( !file.is_open() ) {
// put error handling here...
std::string word;
std::fstream::pos_type startOfWord;
while ( file.peek() != std::fstream::traits::eof() ) {
if ( ::isalpha( file.peek() ) ) {
if ( word.empty() ) {
startOfWord = file.tellg();
}
word += file.get();
} else {
if ( !word.empty() ) {
if ( std::find_if( banned.begin(), banned.end(), CaseInsensitiveCompare() ) ) {
file.seekp( startOfWord );
file.write( std::string( word.size(), '*').c_str(), word.size() );
}
word.clear();
}
file.get();
}
}
with:
struct CaseInsensitiveCompare
{
bool operator()( unsigned char lhs, unsigned char rhs ) const
{
return ::tolower( lhs ) == ::tolower( rhs );
}
bool operator()( std::string const& lhs, std::string const& rhs ) const
{
return lhs.size() == rhs.size()
&& std::equal( lhs.begin(), lhs.end(), rhs.begin(), *this )
}
};
The tellg and seekp probably aren't the most efficient
operations around, but if the file is large, and you don't have
to seek too often, it may still be more efficient than writing
a completely new file. Of course, if efficiency is an issue,
you might want to consider mmap, and doing the job directly in
memory. That would certainly be the most efficient, and
probably the easiest to code as well. But it would be platform
dependent, and would require extra effort to handle files larger
than your available address space.
Also, for the future (since there is a standard tolower that
you can use), when doing code translation (which is really what
to_lower_case does), use a table. It's much simpler and
faster:
char
to_lower_case( char ch )
{
char translationTable[] =
{
// ...
};
return translationTable[static_cast<unsigned char>( ch )];
}
If you don't want your code to be dependent on the encoding, you
can use dynamic initialization:
if ( !initialized ) {
for ( int i = 0; i <= UCHAR_MAX; ++ i ) {
translationTable[i] = i;
}
static char const from[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static char const to[] = "abcdefghijklmnopqrstuvwxyz";
for ( int i = 0; i != sizeof(from); ++ i ) {
translationTable[from[i]] = to[i];
}
}
This is not a good idea for things like tolower, however;
you would have to know all of the possible upper case
characters, which in turn depends on the encoding. (The
functions in <ctype.h> do do something like this. And
redefine the translation table each time you change locale.) It
can be useful for other types of mappings.
I think you need a code to read file word by word and replace if the word is one of BANNED_WORDS
So here is a solution for main():
int main()
{
std::vector <std::string> words; // Vector to hold our words we read in.
std::string str; // Temp string to
std::cout << "Read from a file!" << std::endl;
std::ifstream fin("thisfile.txt"); // Open it up!
while (fin >> str) // Will read up to eof() and stop at every
{ // whitespace it hits. (like spaces!)
words.push_back(str);
}
fin.close(); // Close that file!
std::ofstream fout("temp.txt"); // open temp file
for (int i = 0; i < words.size(); ++i)
{ // replace all words and add it to temp file
filter_word(words.at(i));
fout<<words.at(i) << endl;
}
// Add code for replace the file
return 0;
}
And for to_lower_case() you can use
#include <ctype.h>
// ...
*it = tolower(*it);
As suggested by Paul Evans
Hope this will help you
I have one array like this:
static WCHAR FilesToShow[][100] = { { L"start.cmd" },{ L"image.xml" }, { L"xyz" }};
as you see that there is "xyz" which I have to replace with some unique name. For this I have to read image.xml file.
Please can you tell me how can I do this.
I wrote a method like this:
PRIVATE WCHAR GetSystemName(WCHAR *pName)
{
WCHAR line;
wfstream in("image.xml");
WCHAR tmp;
bool begin_tag = false;
while (getline(in,line))
{
// strip whitespaces from the beginning
for (int i = 0; i < line.length(); i++)
{
if (line[i] == ' ' && tmp.size() == 0)
{
}
else
{
tmp += line[i];
}
}
if (wcswcs(tmp,"<SystemPath>") != NULL)
{
???????? how to get "vikash" from here <SystemPath>C:\Users\rs_user\Documents\RobotStudio\Systems\vikash</SystemPath>
}
else
{
continue;
}
}
return tmp;
}
I'm getting exception for wfstream, getline and line.length() method.
I have included fstream.h header file but I think It's not supported in COM.
Please help me how to solve this issue without parsing xml file.
If your xml-file is simple enough so that there is only a single tag with given name, you could do it like this:
#include <string>
#include <sstream>
#include <iostream>
std::wstring get_value(std::wistream & in, std::wstring const & tagname)
{
std::wstring text = std::wstring(std::istreambuf_iterator<std::wstring::value_type>(in),
std::istreambuf_iterator<std::wstring::value_type>());
std::wstring start_tag = L"<" + tagname + L">";
std::wstring end_tag = L"</" + tagname + L">";
std::wstring::size_type start = text.find(start_tag);
if (start == std::wstring::npos)
{
throw 123;
}
start += start_tag.length();
std::wstring::size_type end = text.find(end_tag);
if (end == std::wstring::npos)
{
throw 123;
}
return text.substr(start, end - start);
}
std::wstring get_substr_after(std::wstring const & str, wchar_t delim)
{
std::wstring::size_type pos = str.rfind(delim);
if (pos == std::wstring::npos)
{
throw 123;
}
return str.substr(pos + 1);
}
void stackoverflow()
{
std::wstring text(L"<foo>\n<bar>abc/def/ghi</bar>\n<baz>123/456/789</baz>\n</foo>\n");
std::wistringstream wiss(text);
std::wcout << text << std::endl;
std::wcout << get_substr_after(get_value(wiss, std::wstring(L"bar")), L'/') << std::endl;
}
The output of this program is:
<foo>
<bar>abc/def/ghi</bar>
<baz>123/456/789</baz>
</foo>
ghi
I hope that answered your question.
you have several issues here.
what you are getting are compiler errors and not exceptions
the header file to include is 'fstream' not 'fstream.h'.
make sure you have a line saying using namespace std;
You are declaring line as a variable of type WCHAR, so it is a single wide character, which surely is not a wstring object. Therefore line.length() is incorrect.
Why are you mixing C (wcswcs()) and C++ (STL) ? maybe you should re-design your function signature.
However, try the below function. I have modified the signature to return a pointer to WCHAR, and place the requested string in the buffer space provided by pName. I added a check to verify that the buffer is large enough to fit the name and the terminating NULL character.
WCHAR* GetSystemName(WCHAR *pName, size_t buflen)
{
wstring line;
wifstream in("image.xml");
WCHAR* tmp = NULL;
while (getline(in,line))
{
// strip whitespaces from the beginning
size_t beg_non_whitespace = line.find_first_not_of(L" \t");
if (beg_non_whitespace != wstring::npos)
{
line = line.substr( beg_non_whitespace );
}
size_t beg_system_path = line.find( L"<SystemPath>" );
if ( beg_system_path != wstring::npos )
{
// strip the tags (assuming closing tag is present)
size_t beg_data = beg_system_path + wstring( L"<SystemPath>" ).length();
size_t range = line.find( L"</SystemPath>" ) - beg_data;
line = line.substr( beg_data, range );
// get file name
size_t pos_last_backslash = line.find_last_of( L'\\' );
if ( pos_last_backslash != wstring::npos )
{
line = line.substr( pos_last_backslash + 1 );
if ( buflen <= line.length() )
{
// ERROR: pName buffer is not large enough to fit the string + terminating NULL character.
return NULL;
}
wcscpy( pName, line.c_str() );
tmp = pName;
break;
}
}
}
return tmp;
}
EDIT: Moreover, if you are using and/or parsing XML in other areas of your program, I strongly suggest using an XML parsing library such as Xerces-C or libXml2.
Thank you all for your answer. Here I got solution of my question.
PRIVATE WCHAR* GetNewSystemName()
{
WCHAR line[756];
WCHAR tempBuffer[100];
CComBSTR path = CurrentFolder.Path();
CComBSTR imagePath1 = L"rimageinfo.xml";
path.AppendBSTR(imagePath1);
std::wfstream in(path);
WCHAR tmp[756];
in.getline(line, 756);
WCHAR* buffer;
buffer = wcswcs(line, L"<SystemPath>");
WCHAR *dest = wcsstr(buffer, L"</SystemPath>");
int pos;
pos = dest - buffer;
unsigned int i = 0;
if (wcswcs(buffer,L"<SystemPath>") != NULL && wcswcs(buffer,L"</SystemPath>") != NULL)
{
for (; i < pos; i++)
{
if (buffer[i] == ' ' && sizeof(tmp) == 0)
{
}
else
{
tmp[i] = buffer[i];
}
}
tmp[i] = NULL;
//break;
}
int j = i;
for (; j > 0; j--)
{
if (tmp[j] == '\\')
{
break;
}
}
j++;
int k = 0;
for (; j < i ; j++)
{
System_Name[k] = tmp[j];
k++;
}
System_Name[k] = NULL;
return System_Name;
I have to read in a csv file with 5 fields (int , char[], char[], char[], float) that looks like that :
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
I have to put the fields in a struct, and then put the struct after one line is complete, into a array of the struct type ...
for the learning effect, we are only allowed to use LOW-LEVEL coding, and only use functions like fgetc, strcpy and no strings, only char[]...
Now I made my algorithm to read the textfile character by character, but I have problems separating them correctly, putting them together again and assigning them to the struct fields correctly. Here is my Code:
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
using namespace std;
int main(int argc, char **argv)
{
struct Stud{
long matrnr;
char vorname[30];
char name[30];
char datum[30];
float note;
};
const int MAX = 30;
Stud stud;
Stud mystud[30]; // <<-- Array of "Stud" type
//memset((void*)mystud,0,sizeof(mystud) * sizeof(Stud));
int wordCounter(0);
int i(0); //thats the charCounter or index
int studentCounter(0);
char wort[MAX];
//int matrnr;
//char vorname[MAX];
//char name[MAX];
//char datum[MAX];
//float note;
FILE * pFile;
int cnr(0);
pFile=fopen("studentendaten.txt","r");
if (pFile==nullptr)
{
perror ("Fehler beim öffnen der Datei");
}
else
{
while (cnr != EOF)
{
(cnr=fgetc(pFile)) ;
if ((char)cnr == '\n') {
mystud[studentCounter] = stud;
studentCounter++;
continue;
}
if ((char)cnr == ';') {
wort[i] = '\0';
switch (wordCounter % 5) {
case 0:
stud.matrnr = atol(wort);
break;
case 1:
strcpy(stud.name, wort);
break;
case 2:
strcpy(stud.vorname, wort);
break;
case 3:
strcpy(stud.datum,wort);
break;
case 4:
stud.note = atof(wort);
break;
}
wordCounter++;
i = 0;
continue;
}
if (wordCounter % 5 == 0 && (char)cnr != ';') {
wort[i] = (char)cnr;
i++;
//stud.matrnr = atol(wort);
}
if (wordCounter % 5 == 1) {
wort[i] = (char)cnr;
i++;
//strcpy(stud.name, wort);
}
if (wordCounter % 5 == 2) {
wort[i] = (char)cnr;
i++;
//strcpy(stud.vorname, wort);
}
if (wordCounter % 5 == 3) {
wort[i] = (char)cnr;
i++;
//strcpy(stud.datum,wort);
}
if (wordCounter % 5 == 4) {
wort[i] = (char)cnr;
i++;
//stud.note = atof(wort);
}
}
fclose (pFile);
}
for (int i(0) ; i <= studentCounter; i++) {
cout <<mystud[i].matrnr << " " << mystud[i].name << " " << mystud[i].vorname <<" "
<< mystud[i].datum <<" " << mystud[i].note << endl;
//printf("%5ld %5s %5s %5s %5f \n",mystud[i].matrnr,mystud[i].name,mystud[i].vorname,mystud[i].datum,mystud[i].note);
}
return 0;
}
I am not sure if it has to do with a wrong increment variables, or the fact that I don't put an '\0' at the end of my wort[] array..and therefore not recognizing the end of my array? And if so, how do I do it without knowing where the end exactly is... ? (I don't know the length of the words..)
EDIT: I updated my code again, the only thing that wonders me is that the LAST LINE IS NOT BEING CORRECTLY PARSED , its showing some rubbish, and I can't see the error in my code...
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
8392019;Thomas;Kretschmer;28.3.1920;2.5;
3471144;Mensch;Arbeit;29.2.2013;4.5;
2039482;Test;Test;30.20.2031;2.0;
7584932;Bau;Maschine;02.02.2010;2.3;
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
8392019;Thomas;Kretschmer;28.3.1920;2.5;
3471144;Mensch;Arbeit;29.2.2013;4.5;
2039482;Test;Test;30.20.2031;2.0;
7584932;Bau;Maschine;02.02.2010;2.3;
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
8392019;Thomas;Kretschmer;28.3.1920;2.5;
3471144;Mensch;Arbeit;29.2.2013;4.5;
2039482;Test;Test;30.20.2031;2.0;
7584932;Bau;Maschine;02.02.2010;2.3;
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
8392019;Thomas;Kretschmer;28.3.1920;2.5;
3471144;Mensch;Arbeit;29.2.2013;4.5;
2039482;Test;Test;30.20.2031;2.0;
7584932;Bau;Maschine;02.02.2010;2.3;
Suggestion: use a case structure for the parsing, and make yourself a "copyToSemicolon" function: then you can write things like
sIndexCount = 0;
char temp[50];
while((cnr=fgetc(pFile)) != EOF) {
offset = 0;
for(var = 0; var < 5; var++ {
switch(var) {
case 0:
offset = copyToSemicolon(temp, cnr, offset) + 1;
stud.matrnr = atoi(temp);
break;
case 1:
offset = copyToSemicolon(mystud[sIndexCount].vorname, cnr, offset) + 1;
break;
... etc
}
}
sIndexCount++;
if(sIndexCount == 50) break; // in case the input file is longer than our structure
}
And you need a function copyToSemicolon that takes two char* pointers as inputs, and that copies characters from the second string (starting at offset) until it reaches either a semicolon or the end of line - and that returns the offset it reached (last character read).
int copyToSemicolon(char* dest, char* source, int offset) {
while(source[offset] != ';' && source[offset] != '\n') {
*dest = source[offset++];
dest++;
}
return offset;
}
EDIT strtok method:
sIndexCount = 0;
char temp[50];
while((cnr=fgetc(pFile)) != EOF) {
offset = 0;
temp = strtok(cnr, ';');
for(var = 0; var < 5; var++ {
switch(var) {
case 0:
stud.matrnr = atoi(temp);
break;
case 1:
strcpy(mystud[sIndexCount].vorname, strtok(NULL, ';'));
break;
... etc
case 4:
mystud[sIndexCount].note = atof(strtok(NULL, '\n'));
}
}
sIndexCount++;
if(sIndexCount == 50) break; // in case the input file is longer than our structure
}
One issue that I am seeing is that your code copies or parses one character at a time, such that when you're reading 2345678;Meier;Hans;12.10.1985;2.4; you first set stud.matrnr to 2, then 23, then 234, then 2345, then 23456, then 234567, then 2345678. Similarly, for stud.name, you first set it to M, then the Me, then to Mei, etc. I propose to you to think of things in a different way. I'll give you some pseudocode:
while (!eof) {
get character from file
if (character isn't ';' and isn't '\n') {
copy character into buffer (increment buffer index)
} else if (character is ';') {
it's the end of a word. Put it in its place - turn it to an int, copy it, whatever
reset the buffer
} else if (character is '\n') {
it's the end of the last word, and the end of the line. Handle the last word
reset the buffer
copy the structure
}
}
This should make life a lot easier on you. You're not changing your data nearly as much, and if you need to debug, you can focus on each part on its own.
Generally, in programming, the first step is making sure you can say in your native speaking language what you want to do, then it's easier to translate it to code. You're close with you implementation, and you can make it work. Just be sure you can explain what should be happening when you see ';' or '\n'.
Since you have tagged this as C++, you should consider using std::getline for reading the line from the file, the use std::getline(file, text_before_semicolon, ';') for parsing the fields.
You could also use std::istringstream for converting the textual representation in the text line to internal numeric format.
I am new to C++ and cannot figure out how to strip some miscellaneous data from a string and then parse it as JSON.
I've ended up using the most documented JSON parser I could find - jansson. It seems excellent, although I'm stuck at the first hurdle.
My program receives a string in the following format:
5::/chat:{"name":"steve","args":[{"connection":"true"}, { "chatbody" : "I am the body" }]}
I've stripped everything outside the curly brackets with:
std::string str=message;
unsigned pos = str.find("{");
std::string string = str.substr (pos);
That leaves:
{
"name": "steve",
"args": [
{
"connection": "true"
},
{
"chatbody": "I am the body"
}
]
}
I'm stuck at stage one parsing this. I have converted the string to a char and then tried to use json_loads, but I don't get anything useful out...
The whole thing looks like this:
void processJson(string message)
{
json_t *root;
json_error_t error;
size_t i;
std::string str=message;
unsigned pos = str.find("{");
std::string str3 = str.substr (pos);
const char * c = str.c_str();
json_t *data, *sha, *name;
root = json_loads(c, 0, &error);
data = json_array_get(root, i);
cout << data;
if(!json_is_object(root))
{
fprintf(stderr, "error: commit data %d is not an object\n", i + 1);
}
}
I need to get the values out, but I just get 01, 02, 03....
is_json_object just says:
error: commit data 1068826 is not an object
error: commit data 1068825 is not an object
error: commit data 1068822 is not an object
What am I doing wrong and how can I properly format this? Ultimately I'll need to iterate over an array but cannot get past this. I'm sure this is just a beginner's mistake.
-EDIT-
Trying to avoid using Boost because of a strict size requirement.
You could always use an existing solution like Boost's property tree, which has a function for automatically parsing JSON files. It's literally as easy as adding these two headers:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
And then adding this small snippet of code in, where jsonfile obviously means your filename.
boost::property_tree::ptree jsontree;
boost::property_tree::read_json(jsonfile, jsontree);
If you ever want to extract information from your JSON tree, you can do it like this, where type is the type of the data you want you extract, and insert.key.path.here is the path to your key, with each parent key separated by periods.
jsonfile.get<type>(insert.key.path.here);
In addition, I don't believe the JSON string you have there is valid. You did good removing the excess around the JSON string itself, but I believe there's a problem here:
"connection" : true,
You can check the validity of your JSON string here: http://jsonformatter.curiousconcept.com/
For JSON formatting, I've searched for a pretty print solution via C++ to no avail. Finally, I found some java code which I eventually converted to C++. Try the following for JSON formatting:
std::string formattedJson(char *json)
{
std::string pretty;
if (json == NULL || strlen(json) == 0)
{
return pretty;
}
std::string str = std::string(json);
bool quoted = false;
bool escaped = false;
std::string INDENT = " ";
int indent = 0;
int length = (int) str.length();
int i;
for (i = 0 ; i < length ; i++)
{
char ch = str[i];
switch (ch)
{
case '{':
case '[':
pretty += ch;
if (!quoted)
{
pretty += "\n";
if (!(str[i+1] == '}' || str[i+1] == ']'))
{
++indent;
for (int j = 0 ; j < indent ; j++)
{
pretty += INDENT;
}
}
}
break;
case '}':
case ']':
if (!quoted)
{
if ((i > 0) && (!(str[i-1] == '{' || str[i-1] == '[')))
{
pretty += "\n";
--indent;
for (int j = 0 ; j < indent ; j++)
{
pretty += INDENT;
}
}
else if ((i > 0) && ((str[i-1] == '[' && ch == ']') || (str[i-1] == '{' && ch == '}')))
{
for (int j = 0 ; j < indent ; j++)
{
pretty += INDENT;
}
}
}
pretty += ch;
break;
case '"':
pretty += ch;
escaped = false;
if (i > 0 && str[i-1] == '\\')
{
escaped = !escaped;
}
if (!escaped)
{
quoted = !quoted;
}
break;
case ',':
pretty += ch;
if (!quoted)
{
pretty += "\n";
for (int j = 0 ; j < indent ; j++)
{
pretty += INDENT;
}
}
break;
case ':':
pretty += ch;
if (!quoted)
{
pretty += " ";
}
break;
default:
pretty += ch;
break;
}
}
return pretty;
}
I'm not familiar with whatever JSON library you're using, but here's a few suggestions about what might be wrong.
size_t i is not initialized to anything, and is then passed into json_array_get.
json_array_get is passed the root object, which is not a JSON array, but a JSON object. In JSON terminology, root["args"] would be the array.
Of course, depending on the semantics of your JSON library, neither of this might be issues at all, but they seem like red flags to me.
Casablanca (REST C++ SDK) has a pretty nice JSON parser which you can use even if you don't use the HTTP functionality.
You can extract the JSON parser files into a static library and link it with your existing project. The files to extract are:
src\json\json.cpp
src\json\json_parsing.cpp
src\json\json_serialization.cpp
src\utilities\asyncrt_utils.cpp
include\cpprest\json.h
include\cpprest\xxpublic.h
include\cpprest\basic_types.h
include\cpprest\asyncrt_utils.h
I can confirm this works as I've used it recently as a static library for my projects.
I also tried to use Jansson but the parser in Casablanca simply feels easier to use and has better Unicode support.
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