Saturday, July 11, 2015

My C++ utilities

When I started using C++, I was very disappointed by the lack of many useful routines that were available to me in Delphi and PHP. So, I had to write most of them myself. Over time, I accumulated my own library with the most useful routines.

They are probably not super-well optimized but they do their job. I don't create standalone applications that are distributed to end users. I write programs to perform specific tasks on my computer – like analyzing data or doing numerical analysis. For these purposes, my routines are doing quite well.

Unfortunately, after switching from Microsoft Visual Studio 2010 to Visual Studio 2013, some of the API functions I was using turned out to be obsolete. I had to clean up what was too old (and what was not very useful anymore). For example, I had a couple of nice date related routines but they all went down with the localtime function.

An important caveat: I am using Visual C++ on Windows so the following code should be easy to use for people who use this platform. Just create anutil.h file containing my code, put the file in the same folder as your source code, and add the #include "anutil.h" line in the beginning of your code. People using Linux will have to pick my code apart, as it is platform dependent - it includes numerous references to Windows API.

Here is a list of the functions (note that some examples require #include <iostream>):

Type conversion

int strtoint(const string &str)
Converts a string into an integer. Inspired by Delphi's strtoint. Example:
int val = strtoint("123");

string inttostr(int val, int length = 0)
Converts an integer into a string. If the resulting string is shorter than the length parameter, then function adds leading zeroes to make up for the difference. Inspired by Delphi's inttostr. Example:
string str = inttostr(8,2); // returns "08"

wchar_t* strtowchar(const string &lan)
Converts a string into a wchar_t*. Note that this requires manually deleting the string pointer later in the code in order to avoid memory leaks. Example:
wchar_t* fname = strtowchar("file.csv");

string wstrtostr(const wstring &lan)
Converts a wstring object into a string object. Example:
WIN32_FIND_DATA search_data;
[…]
filelist.push_back(wstrtostr(search_data.cFileName));

double strtodouble(const string &lan)
Converts a string into a real number. Inspired by Delphi's strtofloat. Example:
double num = strtodouble("23.7");

string doubletostr(double val)
Converts a real number into a string. Inspired by Delphi's floattostr. Example:
string str = doubletostr(17.4); 

String operations

string trim(const string &lan)
Eliminates whitespace characters (space, tab, return, new line) at the beginning and at the end of a string. Inspired by PHP's trim. Example:
string str = trim("  test\r\n\t");

string compact(const string &lan)
Eliminates whitespace characters from the entire string. Example:
string str = compact(" t e s t ");

string str_replace(const string &source, const string &what, const string &dest, TReplacementType rt = RTAll)
Replaces occurrences of a substring in a string with something else. Depending on value of the rt parameters, it can replace all occurrences or only the first or the last occurrence (look up definition of the TReplacementType in my code for details). Inspired by PHP's str_replace. Example:
string str = str_replace("this is a test","is","at");

vector<string> explode(const string &source, const string &separator = "\n")
Converts a string into a vector of strings by breaking the given string at certain spots. The spots are indicated by the separator parameter. Inspired by PHP's explode. Example:
vector<string> vec = explode("Alice\nin\nWonderland");

string implode(const vector<string> &source, const string &separator = "\n")
Merges a vector of strings into a single string by joining the elements of the vector using a given separator string. This operation is opposite to explode.  Inspired by PHP's implode. Example:
string str = implode(vec,"\n");

string htmltostring(const string &source)
Converts piece of HTML code to a text as it is (more or less) displayed by a browser. Example:
std::cout << htmltostring("<b>hey</b> \"<span class=\"q\">Jim</span>\"!");

string htmltocsv(const string &source)
Converts a piece of a HTML code into a string that is ready to be stored in a CSV file format which uses double quotes to delimit strings. Example:
std::cout  << htmltocsv("<b>hey</b> \"<span class=\"q\">Jim</span>\"!");

bool isdigit(char c)
Returns true if the parameter is a digit or false otherwise. Example:
if (isdigit('7')) std::cout << "ok!";

int vecstrfind(const vector<string> &vec, const string &lan)
Returns an index of the vector of strings that points to a given string. Example:
int index = vecstrfind(vec,"Wonderland");

string up(const string& source)
Changes letter case of the string into the upper case. Example:
std::cout << up("Hello World!");

Handling files

bool fileexists(const string &filename)
Checks whether a file with a given file name exists. Example:
if (!fileexists("C:/Windows/System32/kernel32.dll")) std::cout << "oops!";

unsigned int getfilesize(const string &filename)
Returns the size of the file with a given file name. Example:
std::cout << getfilesize("c:/pagefile.sys");

bool download(const string &url, const string &filename)
Downloads a given URL and stores the response in a file. Example:
download("https://en.wikipedia.org/wiki/C%2B%2B","cpp.html");

vector<string> loaddirectory(const string &path)
Searches for files in a directory and returns their list in a vector of string. Wildcard characters are permitted. Example:
vector<string> filez = loaddirectory("c:/windows/system32/*.dll");
for (int i=0;i<filez.size();i++) std::cout << filez[i] << endl;

bool savetofile(const string &content, const string &filename)
Saves string to a file. Example:
savetofile("text,1\nme,2","file.csv");

vector<string> textfile(const string &filename)
Opens a file and reads its content into a vector of strings, each line of the file as a separate string. Inspired by PHP's file. Example:
vector<string> file = textfile("file.csv");
for (int i=0;i<file.size();i++) std::cout << file[i] << endl;

class CCSV
This class wraps a data object that allows for manipulations on a comma delimited file. The file may use (but does not need to) double quotes as text delimiters upon loading. The file will have double quotes as text delimiters upon saving. The data is stored in the data field which is a vector of vectors of strings. Example:
CCSV csv("file.csv");
csv.data[1][1] = "hello";
csv.savetofile("file.csv");

Random numbers

double random()
Generates a real random value uniformly distributed in [0,1). Inspired by PHP's rand. Example:
std::cout << random();

int dice(int minimum, int maximum)
Generates a random integer uniformly distributed between minimum and maximum, inclusive. Inspired by PHP's rand. Example:
std::cout << dice(1,6);

The source code

Download the source code or copy and paste it:

// The file's name is "anutil.h".

#pragma once

#include <sstream>
#include <fstream>
#include <UrlMon.h>
#include <vector>
#include <algorithm>
#include <iomanip>

#pragma comment(lib, "urlmon.lib")

using namespace std;

/* TYPE CONVERSION ROUTINES */

// Converts a string into an integer.
int strtoint(const string &str)  
{
return atoi(str.c_str());
}

// Converts a string object into a wchar_t*.
// Note that this requires manually deleting the string 
// pointer later in the code to avoid memory leaks.
wchar_t* strtowchar(const string &lan)
{
wchar_t *p = new wchar_t[lan.length()+1];
for (int i=0;i<lan.length();i++) p[i] = lan.at(i);
p[lan.length()] = '\0';
return p;
}

// Converts a wstring object into a string object.
string wstrtostr(const wstring &lan)
{
string s(lan.begin(), lan.end());
s.assign(lan.begin(), lan.end());
return s;
}

// Converts an integer value into a string object.
// If the resulting string is shorter than the "length" parameter, 
// then function adds leading zeroes.
string inttostr(int val, int length = 0)
{
stringstream s;
s << val;
string res = s.str();
if (length>res.length())
{
string prefix(length-res.length(),'0');
return prefix+res;
} else return res;
}

// Converts a string object into a real number.
double strtodouble(const string &lan)
{
return atof(lan.c_str());
}

// Converts a real value into a string object.
string doubletostr(double val)
{
stringstream s;
s << setprecision(14) << val;
string res = s.str();
return res;
}

/* STRING PROCESSING ROUTINES */

// Eliminates whitespace characters (space, tab, return, 
// new line) at the beginning and at the end of a string object.
string trim(const string &lan)
{
if (lan.length()==0) return lan;
int i = 0;
while ((lan[i]==' ') || (lan[i]=='\t') || (lan[i]=='\n') || (lan[i]=='\r')) 
{
i++;
if (i==lan.length()) return "";
}
int j = 0;
while ((lan[lan.length()-1-j]==' ') || (lan[lan.length()-1-j]=='\t') 
|| (lan[lan.length()-1-j]=='\n') || (lan[lan.length()-1-j]=='\r')) j++;
return lan.substr(i,lan.length()-i-j);
}

// Eliminates whitespace characters from a entire string.
string compact(const string &lan)
{
string res;
for (int i=0;i<lan.length();i++)
if ((lan[i]!=' ') && (lan[i]!='\t') && (lan[i]!='\n') && (lan[i]!='\r'))
res.push_back(lan[i]);
return res;
}

enum TReplacementType {RTAll, RTFirst, RTLast};

// Replaces occurrences of a substring in a string with something else.
// Depending on value of rt parameters it can replace all occurrences or only 
// first or last occurrence.
string str_replace(const string &source, const string &what, 
const string &dest, TReplacementType rt = RTAll) {
int index = 0;
string res = "";
do
{
int k;
if (rt==RTLast) k = source.rfind(what); else k = source.find(what,index);
if (k<0) return res + source.substr(index);
res += source.substr(index,k-index) + dest;
index = k + what.length();
if (index>=source.length()) return res;
} while (rt==RTAll);
return res + source.substr(index);
}

// Converts a string object into a vector of strings by breaking the given string 
// at certain spots.The spots are given by the separator parameter.
vector<string> explode(const string &source, const string &separator = "\n") {
vector<string> res;
int index = 0;
int k = source.find(separator,index);
while (k>=0)
{
res.push_back(source.substr(index,k-index));
index = k+separator.length();
if (index<source.length()) k = source.find(separator,index); else k = -1;
}
if (index>=source.length()) res.push_back(""); else res.push_back(source.substr(index));
return res;
}

// Merges a vector of strings into a string by joining the elements of a vector 
// with a given separator string. This operation is opposite to explode.
string implode(const vector<string> &source, const string &separator = "\n") {
stringstream s;
for (int i=0;i<source.size();i++) 
{
if (i!=0) s << separator;
s << source[i];
}
return s.str();
}

// This routine converts piece of HTML code to a text as it is 
// (more or less) displayed by a browser.
string htmltostring(const string &source)
{
string lan;
lan = str_replace(source,"&quot;","\"");
lan = str_replace(lan,"&amp;","&");
lan = str_replace(lan,"&#39;","'");
lan = str_replace(lan,"<br />","\n");
lan = str_replace(lan,"&nbsp;"," ");
int lt = lan.find("<");
int gt = lan.find(">",lt);
while ((lt>=0) && (gt>=0))
{
string temp = lan.substr(0,lt);
if (gt+1<lan.size()) temp += " " + lan.substr(gt+1);
lan = temp;
lt = lan.find("<");
gt = lan.find(">",lt);
}
lan = str_replace(lan,"\n"," ");
lan = str_replace(lan,"\r"," ");
lan = str_replace(lan,"\t"," ");
string temp;
if (lan=="") return lan;
temp.push_back(lan[0]);
for (int i=1;i<lan.size();i++)
{
if ((lan[i]==' ') && (temp[temp.size()-1]==' ')) {} else temp.push_back(lan[i]);
}
return temp;
}

// This routine converts a piece of a HTML code into a string that is ready to be 
// stored in a CSV file format which uses double quotes to delimit strings.
string htmltocsv(const string &source)
{
string lan;
lan = htmltostring(source);
lan = str_replace(lan,"\"","\"\"");
lan = "\"" + trim(lan) + "\"";
return lan;
}

// This routine returns true if the parameter is a digit or false otherwise.
bool isdigit(char c)
{
if (((int)c >= 48) && ((int)c <=57)) return true; else return false;
}

// This routine return an index of the vector of strings that points to a given string.
int vecstrfind(const vector<string> &vec, const string &lan)
{
for (int i=0; i<vec.size(); i++) 
{
int w = lan.compare(vec[i]);
if (w==0) return i;
}
return -1;
}

// This routine changes letter case of the string into the upper case.
string up(const string& source)
{
string res = source;
transform(res.begin(),res.end(),res.begin(),::toupper);
return res;
}

/* FILE ROUTINES */

// Checks whether a file with a given filename exists.
bool fileexists(const string &filename)
{
wchar_t* fname = strtowchar(filename);
WIN32_FIND_DATA search_data;
memset(&search_data, 0, sizeof(WIN32_FIND_DATA));
HANDLE handle = FindFirstFile(fname, &search_data);
delete [] fname;
if (handle != INVALID_HANDLE_VALUE) return true; else return false;
}

// Returns the size of the file with a given file name.
unsigned int getfilesize(const string &filename)
{
wchar_t* fname = strtowchar(filename);
WIN32_FIND_DATA search_data;
memset(&search_data, 0, sizeof(WIN32_FIND_DATA));
HANDLE handle = FindFirstFile(fname, &search_data);
delete [] fname;
if (handle != INVALID_HANDLE_VALUE) 
return (search_data.nFileSizeHigh * (MAXDWORD+1)) + search_data.nFileSizeLow;
else return -1;
}

// Downloads a given url and stores the response in a file.
bool download(const string &url, const string &filename)
{
wchar_t filename1[250];
for (int i=0;i<filename.length();i++) filename1[i] = filename.at(i);
filename1[filename.length()] = '\0';
wchar_t url1[250];
for (int i=0;i<url.length();i++) url1[i] = url.at(i);
url1[url.length()] = '\0';
return (URLDownloadToFile(NULL,url1,filename1,0,NULL)==S_OK);
}

// Looks up for files in a directory and returns their list in a vector of string. 
// Wildcard characters are permitted!
vector<string> loaddirectory(const string &path)
{
wchar_t* kupa = strtowchar(path);
WIN32_FIND_DATA search_data;
vector<string> filelist;
memset(&search_data, 0, sizeof(WIN32_FIND_DATA));
HANDLE handle = FindFirstFile(kupa, &search_data); 
delete [] kupa;
while(handle != INVALID_HANDLE_VALUE)
{
filelist.push_back(wstrtostr(search_data.cFileName));
if(FindNextFile(handle, &search_data) == FALSE) break;
}

return filelist;
}

// Saves string to a file. 
bool savetofile(const string &content, const string &filename)
{
ofstream outfile;
outfile.open(filename);
if (!outfile.is_open()) return false;
outfile << content;
outfile.close();
return true;
}

// Opens a file and reads ints content into a vector of strings, 
// each string is a separate line of the file.
vector<string> textfile(const string &filename)
{
ifstream plik(filename,ios::binary);
vector<string> res;
if (!plik.is_open()) return res;
string line;
while (!plik.eof())
{
getline(plik,line);
res.push_back(line);
}
return res;
}

typedef vector<string> TDataRec;
typedef vector<TDataRec> TTable;

// This class represents a data object that allows for manipulations on a comma 
// delimited file with double quotes as text delimiters.
// The data is stored in the TTable object which is a vector of vectors of strings.
class CCSV {
public:
TTable data;
CCSV() {};
CCSV(const string& filename);
bool loadfromfile(const string& filename);
bool savetofile(const string& filename);
};

CCSV::CCSV(const string& filename)
{
loadfromfile(filename);
}

bool CCSV::loadfromfile(const string& filename)
{
ifstream infile(filename);
string line;
while (!infile.eof())
{
getline(infile,line);
if (compact(line)!="") {
int status = 0;  // 0: outside a string; 1: inside a string
string lan = "";
TDataRec rec;
for (int i=0; i<line.size(); i++)
{
switch (status) {
case 0:
{
if (line[i]=='"') 
{
status = 1;
break;
}
if ((line[i]!='"') && (line[i]!=',')) 
{
lan += line[i];
if (i+1==line.size()) 
{
rec.push_back(lan);
lan = "";

break;
}
if (line[i]==',') 
{
rec.push_back(lan);
lan = "";
break;
}
break;
}
case 1:
{
if (line[i]=='"')
{
if ((i+1<line.size()) && (line[i+1]=='"'))
{
i++;
lan += '"';
} else {
i++;
rec.push_back(lan);
lan = "";
status = 0;
}
} else {
lan += line[i];
}
break;
}
}
}
data.push_back(rec);
}
}
infile.close();
return true;
}

bool CCSV::savetofile(const string& filename)
{
ofstream outfile(filename);
for (int i=0; i<data.size(); i++)
{
for (int j=0;j<data[i].size();j++)
{
string lan = "\"" + str_replace(data[i][j],"\"","\"\"") + "\"";
outfile << lan;
if (j+1==data[i].size()) outfile << endl; else outfile << ",";
}
}
outfile.close();
return true;
}

/* RANDOM NUMBER ROUTINES */

// Generates a random real value uniformly distributed in [0,1).
double random()
{
double randmax = (double)(RAND_MAX + 1);
double res = 0;
for (int i=0;i<4;i++) 
{
res += (double)rand();
res = res / randmax;
}
return res;
}

// Generates a random integer uniformly distributed between minimum and maximum, inclusive. 
int dice(int minimum, int maximum)
{
return random()*(maximum-minimum+1)+minimum;
}

More goodies will come.

No comments:

Post a Comment