XML CharacterDataHandler callback unpextedly called multiple times - c

I'm learning about libexpat. I cobbled together this example for basic familiarity using the API:
The Code:
#include <stdio.h>
#include <expat.h>
#include <string.h>
#include <iostream>
void start(void* userData, const char* name, const char* argv[])
{
std::cout << "name: " << name << std::endl;
int i = 0;
while (argv[i])
{
std::cout << "argv[" << i << "] == " << argv[i++] << std::endl;
}
}
void end(void* userData, const char* name)
{
}
void value(void* userData, const char* val, int len)
{
char str[len+1];
strncpy(str, val, len);
str[len] = '\0';
std::cout << "value: " << str << std::endl;
}
int main(int argc, char* argv[], char* envz[])
{
XML_Parser parser = XML_ParserCreate(NULL);
XML_SetElementHandler(parser, start, end);
XML_SetCharacterDataHandler(parser, value);
int bytesRead = 0;
char val[1024] = {};
FILE* fp = fopen("./catalog.xml", "r");
std::cout << "fp == 0x" << (void*)fp << std::endl;
do
{
bytesRead = fread(val, 1, sizeof(val), fp);
std::cout << "In while loop bytesRead==" << bytesRead << std::endl;
if (0 == XML_Parse(parser, val, bytesRead, (bytesRead < sizeof(val))))
{
break;
}
}
while (1);
XML_ParserFree(parser);
std::cout << __FUNCTION__ << " end" << std::endl;
return 0;
}
catalog.xml:
<CATALOG>
<CD key1="value1" key2="value2">
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<YEAR>1995</YEAR>
</CD>
</CATALOG>
Makefile:
xml: xml.o
g++ xml.o -lexpat -o xml
xml.o: main.cpp Makefile
g++ -g -c main.cpp -o xml.o
Output:
fp == 0x0x22beb50
In while loop bytesRead==148
name: CATALOG
value:
value:
name: CD
argv[1] == key1
argv[2] == value1
argv[3] == key2
argv[4] == value2
value:
value:
name: TITLE
value: Empire Burlesque
value:
value:
name: ARTIST
value: Bob Dylan
value:
value:
name: YEAR
value: 1995
value:
value:
value:
In while loop bytesRead==0
main end
Question:
From the output, it appears that the callback I installed with XML_SetCharacterDataHandler() gets called twice for the CATALOG,, CD, TITLE, and ARTIST xml tags, and then multiple times for the YEAR tag - can someone explain this behavior? From the noted catalog.xml, it's not clear to me why there are (or would ever be) multiple values associated with any XML tags.
Thank you.
Citation:
Credit to this site for the basis of the above sample code.

The expat parser may split text nodes into multiple calls to the character data handler. To properly handle text nodes you must accumulate text over multiple calls and process it when receiving the "end" event for the containing tag.
This is true in general, even across different parsers and different languages -- i.e. the same thing is true in Java.
See for instance http://marcomaggi.github.io/docs/expat.html#using-comm
A common first–time mistake with any of the event–oriented interfaces to an XML parser is to expect all the text contained in an element to be reported by a single call to the character data handler. Expat, like many other XML parsers, reports such data as a sequence of calls; there's no way to know when the end of the sequence is reached until a different callback is made.
Also from the expat documentation
A single block of contiguous text free of markup may still result in a sequence of calls to this handler. In other words, if you're searching for a pattern in the text, it may be split across calls to this handler.

Related

Writing c-style string to text file and returning information from text file

I'm trying to write a program that creates a structure for student info, has an array with 4 students info, writes the info to a text file, and returns the information back from the text file. I'm also supposed to do the same process with binary I/O, but that part works well so I took it out of the code. My issue is with the text file. I believe the issue is with the name part of the array. I also have the guideline to follow: "Do not use the string class in your struct definition. Instead, use the old c-style string (i.e. array of char)" I can't figure out how to get the program to step over the space between first/last names. This is the code I have written:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstring>
using namespace std;
/* structure */
struct studentInfo
{
char name[40];
int age;
float gpa;
char grade;
};
int main(int argc, const char * argv[])
{
const int SIZE = 4;
int i;
/* array */
studentInfo student[SIZE]{ {"Ann Annson\0", 10, 1.10, 'D'},
{"Bill Billson\0", 20, 2.20, 'C'},
{"Carl Carlson\0", 30, 3.30, 'B'},
{"Don Donson\0", 40, 4.00, 'A'} };
/* open & write to file in text mode i/o */
fstream fFile;
fFile.open("students.txt", ios::out);
for(i = 0; i < SIZE; i++)
{
fFile << student[i].name << endl;
fFile << student[i].age << endl;
fFile << student[i].gpa << endl;
fFile << student[i].grade << endl;
}
fFile.close();
/* open and read information from files to new arrays */
studentInfo studentsText[4];
fFile.open("students.txt", ios::in);
for(i = 0; i < SIZE; i++)
{
fFile >> studentsText[i].name;
fFile >> studentsText[i].age;
fFile >> studentsText[i].gpa;
fFile >> studentsText[i].grade;
}
fFile.close();
/* display the information from students.txt */
cout << "This is the data contained in students.txt: " << endl;
for (i = 0; i < SIZE; i++)
{
cout << studentsText[i].name << "\t" << studentsText[i].age;
cout << "\t" << studentsText[i].gpa << "\t" << studentsText[i].grade << endl;
}
return 0;
}
this is the output that I receive when I run the program:
screenshot
and this is the output I'm trying to get:
screenshot
I do know that it is an issue regarding the space, because if I run the program with the spaces between the names in the array removed it returns this output: screenshot
Forgive me if this is a poor question, I'm in my first semester of c++ and I hope that I worded this well for anyone that attempts to help. Thank you to everyone who looks at this and tries to help!
side note
this is the full code I have written
and this is the output I receive when running that code

sscanf with double quotes

I'm using an Arduino and Open Weather Map API to create a weather station, but I'm having serious trouble to parse the response into something useful with sscanf.
Here is one response example:
{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"base":"cmc stations","main":{"temp":14.17,"pressure":1012,"humidity":74,"temp_min":13,"temp_max":15.8},"wind":{"speed":4.6,"deg":150},"clouds":{"all":0},"dt":1459602835,"sys":{"type":1,"id":5091,"message":0.0059,"country":"GB","sunrise":1459575095,"sunset":1459622222},"id":2643743,"name":"London","cod":200}
I would like to parse the weather info (Clear) from:
"weather":[{"id":800,"main":"Clear",
And the temp info (14) from:
"main":{"temp":14.17,
This is the code, I'm using:
if (character == '}') { // Just a delimiter
if (strstr(response, "\"weather\":[{")) { // to confirm that the string was found
sscanf(response, ",main\":%s,", weather);
Serial.printfn("\r\nfound weather = %s"), weather;
}
else if (strstr(response, "\"main\":{\"temp\":")) { // to confirm that the string was found
sscanf(response, "temp\":%2s,", temp);
Serial.printfn("\r\nfound temp = %s"), temp;
}
memset(response, 0, sizeof(response));
idx = 0;
}
But the sscanf seens to not even beeing working, since it always print the whole weather / temp strings 32 bytes long.
found weather = ,"weather":[{"id":800,"main":"Clear","description":
found temp = ],"base":"cmc stations","main":{"temp":14.17,"pressure":1011,"humi
Anyone have any clue how to parse these string using sscanf?
Here is an example. Translate it to any C-Dialect you need.
#include <cstdio>
#include <cstring>
#include <iostream>
const char* haystack = "\"weather\":[{\"id\":800,\"main\":\"Clear\",";
const char* needle = "\"main\":";
int main()
{
std::cout << "Parsing string: '" << haystack << "'\n";
if (const char* cursor = strstr(haystack, needle)) {
char buffer[100];
if (sscanf(cursor, "\"main\":\"%99[^\"]\",", buffer))
std::cout << "Parsed string: '" << buffer << "'\n";
else
std::cout << "Parsing error!\n";
} else {
std::cout << "Could not find '" << needle << "' in '" << haystack << "'\n";
}
}
if Serial.printfn is a pointer to a function which work like printf(), then
Serial.printfn("\r\nfound weather = %s"), weather;
is undefined Behavior, and may print what you see.
you should use
Serial.printfn("\r\nfound weather = %s", weather);

Recover location information from trace using bfd

I am using the dladdr from libld (http://linux.die.net/man/3/dladdr) to get a trace of the function calls. Here it is minimal example with a single traced element:
#include<iostream>
#include <dlfcn.h> // link with -ldl -rdynamic a
void f(){
void **frame = static_cast<void **>(__builtin_frame_address(0));
void **bp = static_cast<void **>(*frame);
void *ip = frame[1];
Dl_info info;
dladdr(ip, &info);
std::cout << info.dli_sname << " " << info.dli_fname << " " << info.dli_saddr << std::endl;
ip = bp[1];
bp = static_cast<void**>(bp[0]);
dladdr(ip, &info);
std::cout << info.dli_sname << " " << info.dli_fname << " " << info.dli_saddr << std::endl;
}
int main(){
f();
}
which outputs:
main ./a.out 0x402800
__libc_start_main /lib64/libc.so.6 0x7febf6bf2610
that is, Dl_info has the traced function name, the compiled file where it belongs and some address (0x7f...) described in the man page as "Exact address of symbol named".
This address has information of the source file location (from where the function has been called). In fact with the help of some utility I can get that information:
$ addr2line -e a.out
/home/user/test.cpp:34
(give the exact line where main is defined in source file). And this works as long as the program was compiled with the -g option.
Now what I want is to extract this information programmaticaly. Supposedly, this is possible with the BFD library.
This is my attempt, based on BFD examples found for example here: http://opensource.apple.com/source/X11libs/X11libs-40.2/cairo/cairo-1.8.6/util/backtrace-symbols.c
1) first I have to define a function find_addr_sect that will be called by bfd_map_over_sections (through a pointer) later.
static void find_addr_sect(bfd *abfd, asection *section, void *obj){
bfd_data *data = (bfd_data *)obj;
bfd_vma vma;
bfd_size_type size;
if (data->found)
return;
if (!(bfd_get_section_vma(abfd, section)))
return;
vma = bfd_get_section_vma(abfd, section);
if (data->pc < vma)
return;
size = bfd_get_section_size(section);
if (data->pc >= vma + size)
return;
data->found = bfd_find_nearest_line(abfd, section, syms,
data->pc - vma,
&data->filename,
&data->function,
&data->line);
}
2) I put the code directing inside the function (this replaces the function void f() above.
void f(){
void **frame = static_cast<void **>(__builtin_frame_address(0));
void **bp = static_cast<void **>(*frame);
void *ip = frame[1];
Dl_info info;
dladdr(ip, &info);
std::cout << info.dli_sname << " " << info.dli_fname << " " << info.dli_saddr << std::endl;
////////////////////
// this will try to find the location of main (first in the stack)
bfd *abfd = bfd_openr(info.dli_fname, NULL); assert(abfd); // the executable file is opened successfully
// bfd_data data;
bfd_map_over_sections(abfd, find_addr_sect, nullptr); // !! doesn't call `find_addr_sect` at all.
///////////////////
ip = bp[1];
bp = static_cast<void**>(bp[0]);
dladdr(ip, &info);
std::cout << info.dli_sname << " " << info.dli_fname << " " << info.dli_saddr << std::endl;
}
Sadly, I am stuck here because the bfd_map_over_sections call doesn't do anything. I am using bfd_map_over_sections in the wrong way, why?
Sorry for using C++, in this is a C question. It shortens most of my code and I am more used to it.
EDIT: I added this lines and I can confirm that one clue of the problem is that the number of sections is zero.
unsigned int numSections = -1;
numSections = bfd_count_sections(abfd);
std::cout << "num sections " << numSections << std::endl; // gives "0"
I looked for more examples, it seems that I was missing two things, calling the function bfd_check_format after opening and also populating and passing the address information in the bfd_data structure.
...
bfd *abfd = bfd_openr(info.dli_fname, NULL); assert(abfd);
// char **matching;
// bfd_data data;// = (bfd_data *)obj;
if (!bfd_check_format (abfd, bfd_object)){
bfd_close (abfd); assert(0);
}
...
later bfd_data variable is used as input and output of the find_addr_sect. Therefore
...
bfd_data data;// = (bfd_data *)obj;
data.pc = (bfd_hostptr_t)info.dli_saddr;
data.found = FALSE;
bfd_map_over_sections(abfd, find_addr_sect, &data);
...
And now it works.

Need Help Removing Entries from a Large Text File Based on the Contents of Another Text File

Good day. I could really use your help on this one. I have a stats text file in the following format.
ID=1000000
Name=Name1
Field1=Value1
...(Fields 2 to 25)
Field26=Value26
ID=1000001
Name=Name2
Field1=Value1
...(Fields 2 to 25)
Field26=Value26
ID=1000002
Name=Name2
Field1=Value1
...(Fields 2 to 25)
Field26=Value26
...goes up to 15000
I have an active people text file separated by line breaks.
Name2
Name5
Name11
Name12
...goes up to 1400 Random Names
I need to be able to delete records from the stats text file (ID, Name, Fields1 to 26) if the name is not found in the active people text file. In the example above, the associated record for Name1(ID, Name, Fields1 to 26) should be deleted since it's not in the active people text file.
I've tried reformatting the stats file through notepad++ using TextFX->Quick->Find/Replace to convert it to a comma separated file with each record separated by a line break. I had it rearranged to
ID Name Field1 ...Fields2 to Fields 25... Field26
1000000 Name1 Value1 ...Value2 to Value 25... Value26
1000001 Name2 Value1 ...Value2 to Value 25... Value26
1000002 Name2 Value1 ...Value2 to Value 25... Value26
I've opened it with excel and I've created two tables (stats table and a active names table) in mysql using the csv file file. I'm not sure how to process this in an automatic function. Besides removing inactive records, the other problem I have is rewriting it back to its old format.
I've been trying my best to figure this out for a hours on end. Is there a solution that won't require me to use find, copy, paste and switch between the two files 1400 times? Unfortunately, I have to keep the stats file in this format.
Please help. Thank you.
Here's a C++ program that will process the files for you:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <locale>
#include <set>
#include <string>
#include <vector>
//trim functions taken:
//http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring/217605#217605
//with a slight change because of trouble with ambiguity
static int myIsSpace(int test)
{
static std::locale loc;
return std::isspace(test,loc);
}
static std::string &rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(myIsSpace))).base(), s.end());
return s;
}
static std::string &ltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(myIsSpace))));
return s;
}
static std::string &trim(std::string &s) {return ltrim(rtrim(s));}
int main(int argc,char * argv[])
{
std::ifstream peopleFile;
peopleFile.open("people.txt");
if (!peopleFile.is_open()) {
std::cout << "Could not open people.txt" << std::endl;
return -1;
}
std::set<std::string> people;
while (!peopleFile.eof()) {
std::string somePerson;
std::getline(peopleFile,somePerson);
trim(somePerson);
if (!somePerson.empty()) {
people.insert(somePerson);
}
}
peopleFile.close();
std::ifstream statsFile;
statsFile.open("stats.txt");
if (!statsFile.is_open()) {
std::cout << "could not open stats.txt" << std::endl;
return -2;
}
std::ofstream newStats;
newStats.open("new_stats.txt");
if (!newStats.is_open()) {
std::cout << "could not open new_stats.txt" << std::endl;
statsFile.close();
return -3;
}
size_t totalRecords=0;
size_t includedRecords=0;
bool firstRecord=true;
bool included=false;
std::vector<std::string> record;
while (!statsFile.eof()) {
std::string recordLine;
getline(statsFile,recordLine);
std::string trimmedRecordLine(recordLine);
trim(trimmedRecordLine);
if (trimmedRecordLine.empty()) {
if (!record.empty()) {
++totalRecords;
if (included) {
++includedRecords;
if (firstRecord) {
firstRecord=false;
} else {
newStats << std::endl;
}
for (std::vector<std::string>::iterator i=record.begin();i!=record.end();++i) {
newStats << *i << std::endl;
}
included=false;
}
record.clear();
}
} else {
record.push_back(recordLine);
if (!included) {
if (0==trimmedRecordLine.compare(0,4,"Name")) {
trimmedRecordLine=trimmedRecordLine.substr(4);
ltrim(trimmedRecordLine);
if (!trimmedRecordLine.empty() && '='==trimmedRecordLine[0]) {
trimmedRecordLine=trimmedRecordLine.substr(1);
ltrim(trimmedRecordLine);
included=people.end()!=people.find(trimmedRecordLine);
}
}
}
}
}
if (!record.empty()) {
++totalRecords;
if (included) {
++includedRecords;
if (firstRecord) {
firstRecord=false;
} else {
newStats << std::endl;
}
for (std::vector<std::string>::iterator i=record.begin();i!=record.end();++i) {
newStats << *i << std::endl;
}
included=false;
}
record.clear();
}
statsFile.close();
newStats.close();
std::cout << "Wrote new_stats.txt with " << includedRecords << " of the " << totalRecords << ((1==totalRecords)?" record":" records") << "found in stats.txt after filtering against the " << people.size() << ((1==people.size())?" person":" people") << " found in people.txt" << std::endl;
return 0;
}

SQLite in C and supporting REGEXP

I'm using sqlite3 in C and I'd like to add support for the REGEXP operator. By default, a user defined function regexp() is not present and calling REGEXP will usually result in an error (according to the SQLite pages).
How do I add a regexp function to support REGEXP? Presumably I will do this via the sqlite3_create_function call, but I don't know what the application-defined regexp() will look like.
Can I use a function from regex.h with sqlite3_create_function and how? Any function I pass to SQLite has to take three arguments of type sqlite3_context*, int, sqlite3_value**. However, the SQLite documents don't seem to explain the meaning of these parameters.
Is there sample code for a C regexp() function?
I've not been able to find much on this using Google or the SQLite pages.
You can also try this:
#include <regex.h>
...
void sqlite_regexp(sqlite3_context* context, int argc, sqlite3_value** values) {
int ret;
regex_t regex;
char* reg = (char*)sqlite3_value_text(values[0]);
char* text = (char*)sqlite3_value_text(values[1]);
if ( argc != 2 || reg == 0 || text == 0) {
sqlite3_result_error(context, "SQL function regexp() called with invalid arguments.\n", -1);
return;
}
ret = regcomp(&regex, reg, REG_EXTENDED | REG_NOSUB);
if ( ret != 0 ) {
sqlite3_result_error(context, "error compiling regular expression", -1);
return;
}
ret = regexec(&regex, text , 0, NULL, 0);
regfree(&regex);
sqlite3_result_int(context, (ret != REG_NOMATCH));
}
...
sqlite3_create_function(*db, "regexp", 2, SQLITE_ANY,0, &sqlite_regexp,0,0)
It would look something like this:
static void user_regexp(sqlite3_context *context, int argc, sqlite3_value **argv)
{
struct re_pattern_buffer buffer;
const char *out;
char *pattern;
char *input_string;
char *result;
struct re_registers regs;
if ((sqlite3_value_type(argv[0]) != SQLITE_TEXT )
|| ((sqlite3_value_type(argv[1]) != SQLITE_TEXT ))
{
sqlite3_result_err("Improper argument types");
return;
}
re_set_syntax(RE_SYNTAX_POSIX_EGREP);
memset(&buffer, 0, sizeof (buffer));
if (!(pattern = strdupa(sqlite3_value_text(argv[0])))
|| !(input_string = strdupa(sqlite3_value_text(argv[1]))))
{
sqlite3_result_err_nomem("Could not allocate memory for strings");
return;
}
if ((out = re_compile_pattern(pattern, strlen(pattern), &buffer))
{
sqlite3_result_err("Could not compile pattern!");
return;
}
if (re_match(&buffer, input_string, strlen(input_string), 0, &regs) < 0)
sqlite3_result_int64(context, 0);
else
{
result = strndupa(input_string + regs.start[0], regs.end[0] - regs.start[0]);
sqlite3_result_text(context, result, NULL, SQLITE_TRANSIENT);
}
}
Okay, a bit too late for this but I'm tempted to post this for all people who are using a C++ Wrapper for the C SQLITE API like the [ SQLiteCpp ] which I am using. This answer assumes that you use SQLiteCpp.
Install Regex for windows binaries from [ here ]. This gives you just enough files ie the regex.h include file and regex2.dll. Do remember to add the path regex.h in your project and have a copy of the dll in the folder containing client executables.
Before building the [ SQLiteCpp ], we need to make some changes to add the regex capabilities to SELECT queries. For this open the Database.cpp file from the [ SQLiteCpp ] project and
Include the regex.h header from the Regex for windows
After all the includes, add below ( of course you can customize to custom fit your needs!) piece of code just below it.
extern "C" {
void sqlite_regexp(sqlite3_context* context, int argc, sqlite3_value** values) {
int ret;
regex_t regex;
char regtext[100];
char* reg = (char*)sqlite3_value_text(values[0]);
sprintf(regtext, ".*%s.*", reg);
//printf("Regtext : %s", regtext);
char* text = (char*)sqlite3_value_text(values[1]);
/* printf("Text : %s\n", text);
printf("Reg : %s\n", reg); */
if (argc != 2 || reg == 0 || text == 0) {
sqlite3_result_error(context, "SQL function regexp() called with invalid arguments.\n", -1);
return;
}
ret = regcomp(&regex, regtext, REG_EXTENDED | REG_NOSUB | REG_ICASE);
if (ret != 0) {
sqlite3_result_error(context, "error compiling regular expression", -1);
return;
}
ret = regexec(&regex, text, 0, NULL, 0);
/* if (ret == 0) {
printf("Found a match. Press any key to continue");
getc(stdin);
}*/
regfree(&regex);
sqlite3_result_int(context, (ret != REG_NOMATCH));
}
}
Now it is time to change the constructors defined in the file. Change those like shown below.
// Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags.
Database::Database(const char* apFilename,
const int aFlags /*= SQLite::OPEN_READONLY*/,
const int aBusyTimeoutMs/* = 0 */,
const char* apVfs/*= NULL*/) :
mpSQLite(NULL),
mFilename(apFilename)
{
const int ret = sqlite3_open_v2(apFilename, &mpSQLite, aFlags, apVfs);
//std::cout << "Reached here";
//sqlite3_create_function_v2(mpSQLite, "REGEXP", 2, SQLITE_ANY,&sqlite_regexp, NULL, NULL, NULL,NULL);
sqlite3_create_function(mpSQLite, "regexp", 2, SQLITE_ANY, 0, &sqlite_regexp, 0, 0);
if (SQLITE_OK != ret)
{
const SQLite::Exception exception(mpSQLite, ret); // must create before closing
sqlite3_close(mpSQLite); // close is required even in case of error on opening
throw exception;
}
else {
}
if (aBusyTimeoutMs > 0)
{
setBusyTimeout(aBusyTimeoutMs);
}
}
// Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags.
Database::Database(const std::string& aFilename,
const int aFlags /* = SQLite::OPEN_READONLY*/,
const int aBusyTimeoutMs/* = 0*/,
const std::string& aVfs/* = "" */) :
mpSQLite(NULL),
mFilename(aFilename)
{
const int ret = sqlite3_open_v2(aFilename.c_str(), &mpSQLite, aFlags, aVfs.empty() ? NULL : aVfs.c_str());
sqlite3_create_function(mpSQLite, "regexp", 2, SQLITE_ANY, 0, &sqlite_regexp, 0, 0);
if (SQLITE_OK != ret)
{
const SQLite::Exception exception(mpSQLite, ret); // must create before closing
sqlite3_close(mpSQLite); // close is required even in case of error on opening
throw exception;
}
if (aBusyTimeoutMs > 0)
{
setBusyTimeout(aBusyTimeoutMs);
}
}
By now, you've some serious regex capabilities with your sqlite. Just build the project.
Write a client program to test the functionality. It can be something like below ( borrowed without shame from SQLiteCpp Example ).
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <SQLiteCpp/SQLiteCpp.h>
#include <SQLiteCpp/VariadicBind.h>
// Notice no sqlite3.h huh?
// Well, this is a C++ wrapper for the SQLITE CAPI afterall.
#ifdef SQLITECPP_ENABLE_ASSERT_HANDLER
namespace SQLite
{
/// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt)
void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg)
{
// Print a message to the standard error output stream, and abort the program.
std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message \"" << apMsg << "\"\n";
std::abort();
}
}
#endif
/// Get example path
static inline std::string getExamplePath()
{
std::string filePath(__FILE__);
return filePath.substr(0, filePath.length() - std::string("Client.cpp").length());
}
/// Example Database
static const std::string filename_example_db3 = getExamplePath() + "/example.db3";
/// Image
static const std::string filename_logo_png = getExamplePath() + "/logo.png";
/// Object Oriented Basic example
class Example
{
public:
//Constructor
Example() :
mDb(filename_example_db3),
// User change the db and tables accordingly
mQuery(mDb, "SELECT id,name FROM lookup WHERE name REGEXP :keyword")
// Open a database file in readonly mode
{
}
virtual ~Example()
{
}
/// List the rows where the "weight" column is greater than the provided aParamValue
void namehaskeyword(const std::string searchfor)
{
std::cout << "Matching results for " << searchfor << "\n";
// Bind the integer value provided to the first parameter of the SQL query
mQuery.bind(1,searchfor); // same as mQuery.bind(1, aParamValue);
// Loop to execute the query step by step, to get one a row of results at a time
while (mQuery.executeStep())
{
std::cout<<mQuery.getColumn(0) << "\t" << mQuery.getColumn(1) << "\n";
}
// Reset the query to be able to use it again later
mQuery.reset();
}
private:
SQLite::Database mDb; ///< Database connection
SQLite::Statement mQuery; ///< Database prepared SQL query
};
int main()
{
// Using SQLITE_VERSION would require #include <sqlite3.h> which we want to avoid: use SQLite::VERSION if possible.
// std::cout << "SQlite3 version " << SQLITE_VERSION << std::endl;
std::cout << "SQlite3 version " << SQLite::VERSION << " (" << SQLite::getLibVersion() << ")" << std::endl;
std::cout << "SQliteC++ version " << SQLITECPP_VERSION << std::endl;
try
{
// Doing a regex query.
Example example;
char wannaquit = 'n';
std::string keyword;
// Deliberate unlimited loop. You implement something sensible here.
while (wannaquit != 'y') {
// Demonstrates the way to use the same query with different parameter values
std::cout << "Enter the keyword to search for : ";
std::getline(std::cin, keyword);
example.namehaskeyword(keyword);
}
}
catch (std::exception& e)
{
std::cout << "SQLite exception : " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
return EXIT_SUCCESS;
}
Note : This assumes that the database is in the same folder as your cpp

Resources