|
25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more... |
Home Projects Up Sitemap Search Blog Forum+Chat About Us Privacy Terms of Use Feedback FAQ Images Services Ads Donate Humor |
|
|
mylib.cpp for MS Visual C++ 2005/2008#include this file to pull its library routines into your C++ program. This file is converted for use with Microsoft Visual C++ and its Standard Template Library (STL). It compiles, but some of the functions haven't been tested since the conversion. They are being tested as I encounter them in other programs being converted. Unlike the previous Borland version of mylib.cpp, this version contains the full source code instead of #includes that reference external files. The one external file it does #include is my.h. The date on the first line of the listing is the "version number". Additions are being made fairly often while converting the programs that use it to MSVC++. |
|
/* mylib.cpp 6-26-2008
MS Visual C++ Version.
Copyright (C)1997-2000, 2006-08 Steven Whitney.
Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.
#including this file pulls in the source code for the library functions.
------
NOTES:
--It is converted and compiles, but most of the fns remain untested.
*/
#include "my.h"
using namespace std;
using namespace System;
//----------------------------------------------------------------------------
void SystemStringToBasicString(String^ src, string& dest)
{
using namespace Runtime::InteropServices;
const char* p = (const char*)((Marshal::StringToHGlobalAnsi(src)).ToPointer());
dest = p;
Marshal::FreeHGlobal(IntPtr((void*)p));
}
//----------------------------------------------------------------------------
/* Creates a backup copy of a file.
Given the name of a file you want to backup before writing to, it deletes filename.bak (the
current backup file), renames the current filename.ext to filename.bak (backs it up), and
returns a reference to the original filename.ext string.
It should work properly with full path names.
Use: ofstream outfile(backupfile(filename.ext).c_str());
*/
const string& backupfile(const string& filename)
{
if(filename.length())
{
string bakfile(filename);
if(bakfile.find(".") != string::npos) // delete extension
bakfile.erase(bakfile.rfind("."));
bakfile += ".bak"; // add ".bak"
_unlink(bakfile.c_str()); // delete .bak file, if any
rename(filename.c_str(),bakfile.c_str()); // rename current to .bak
}
return(filename); // return reference to original filename
}
//----------------------------------------------------------------------------
//counts number of occurrences of a char in a string. Pulled from code.cpp.
uint countchr(const string& s, char ch) // string to search, char to look for
{
int count = 0;
int N = s.length();
for(int i = 0 ; i < N ; i++)
if(s[i] == ch)
count++;
return(count);
}
//----------------------------------------------------------------------------
/* Returns true if char array is zero-length or all whitespace, false otherwise.
Could add additional version for a const string&
*/
bool isallspace(char *s)
{
char *p = s;
while(*p)
if(!isspace(*(p++))) // first non-space char causes return
return(false);
return(true);
}
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
/* functions for appending strings to text files:
logs an error string to an error log file, and keeps count.
*/
//-----------------------------------------------------------------------
// append a string to a text file.
uint logtofile(const string& s, const string& filename)
{
if(s.length()&& filename.length())
{
ofstream(filename.c_str(),ios::app) << s << endl;
return(1);
}
return(0);
} //logtofile
//-----------------------------------------------------------------------
// write error (if provided) to disk. returns cumulative error count.
// you can call with empty string just to get the count.
uint logerror(string error, string filename, bool beep)
{
static uint errorcount = 0; // # of errors
if(error.length()) // if an error occurred,
{
ostrstream os;
os << time(NULL) << ": " << error << ends;
logtofile(os.str(),filename);
//if(beep) // note: only beeps if we're writing an error
//{
// #if defined(_Windows)
// MessageBeep(MB_ICONEXCLAMATION); // a global WinSDK function
// #else
// cout << "\a";
// // SerialCom.Send(os.str());
// #endif
//}
delete[] os.str();
errorcount++;
}
return(errorcount);
} //logerror
//////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
// duplicates of this may exist local to some project. also keep a watch for places where I
// now round incorrectly or not at all, and could use this instead.
// usage: int x = round(speed * sine(30) * x);
int round(double d)
{
if(d > 0) // if positive, make more positive before rounding
d += .5;
else
if(d < 0) // if negative, make more negative before rounding
d -= .5;
return((int)d); // truncates decimal portion
}
//----------------------------------------------------------------------------
/* 5-8-2007
returns a uniformly distributed random integer in the range 0 (inclusive) to i-1,
The random sequence seems to meet the criteria it should, using Excel stats analysis.
lrandom() below is better but its method is slower.
*/
unsigned random(unsigned i)
{
if(i < 2)
return(0);
else
return (unsigned)(((long)rand() * i) / (RAND_MAX + 1));
} //random
//----------------------------------------------------------------------------
/* returns a uniformly distributed random double in the range 0 (inclusive) to 1.0 (limit).
builds as text because that's the only way I could think of that seems foolproof.
There are pitfalls to generating random numbers.
this is very fast, and works for DOS, Win16, and Win32.
this method seems to work properly, based on running the results through stats.exe.
*/
double frand()
{
int len = 17; // "0." plus 15 significant digits is max possible for a double.
char buf[18]; // must be len + 1
buf[0] = '0'; buf[1] = '.';
for(int i = 2 ; i < len ; i++)
{
// you cannot use random() here because in Win32 random() calls lrandom(), which
// calls this, creating an infinite recursion, stack overflow, and crash.
// the method highlighted here is equivalent to random(10).
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
buf[i] = (char)(0x30 + (int)( ((long)rand() * 10) / (RAND_MAX + 1) ));
}
buf[len] = 0;
return(atof(buf));
} //frand
//----------------------------------------------------------------------------
/* Returns a long random number from 0 to specified upper limit (top) - 1, inclusive.
This is random() for longs, and I use it as replacement random() for Win32.
For a possibly negative random number, use this, and then if(random(2)) { make it negative }
this is very fast, and seems to work, based on running the results through stats.exe.
In Win32 only, Borland has a function _lrand(), which returns a random long
value (probably 0 to LONG_MAX), but you can't scale it to a different interval
by the usual method because the result of the required multiplication can
overflow LONG_MAX and even ULONG_MAX, and that's the largest integral value
the computer can handle. Borland's stdlib.h method of using % operator doesn't
work because its random(N) has a repeat cycle of N if N is a power of 2, probably
because of coincidence(?) with cycles inherent in the random # generation.
*/
long lrandom(long top)
{
if(top <= 1L) // 0, 1 always produce 0, and negative is illegal
return(0L);
return((long)(frand() * top));
} //lrandom
//----------------------------------------------------------------------------
/* returns a normally distributed random INTEGER from the distribution with the
given mean and standard deviation, using the method from the TI-59 calculator book
TI Programmable 58/59 Applied Statistics using the power of your Solid State Software(tm)
module, (C)1977 Texas Instruments, p.2-6.
*/
int NormalRandom(double mean, double stddev)
{
return round(sqrt(-2.0 * log(frand())) * cos(2.0 * M_PI * frand()) * stddev + mean);
} //NormalRandom
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
// (MYSTRING.CPP) STRING FUNCTIONS
//----------------------------------------------------------------------------
// a replacement for Borland string::contains())
// returns true if the string contains the given substring, else false.
bool stringcontains(string& s, string& tofind)
{
return(s.find(tofind) != string::npos);
} // stringcontains
//----------------------------------------------------------------------------
// a replacement for Borland string::contains())
// returns true if the string contains the given substring, else false.
bool stringcontains(string& s, const char* tofind)
{
return(s.find(tofind) != string::npos);
} // stringcontains
//----------------------------------------------------------------------------
// a replacement for Borland string::contains())
// returns true if the string contains the given char, else false.
bool stringcontains(string& s, char tofind)
{
for(uint i = 0 ; i < s.length() ; i++)
if(s[i] == tofind)
return true;
return false;
} // stringcontains
//----------------------------------------------------------------------------
// change a string to upper case. (a replacement for Borland string::to_upper())
string& toupper(string& s)
{
uint i;
for(i = 0 ; i < s.length() ; i++)
s[i] = (char)toupper(s[i]);
return(s);
} // toupper
//----------------------------------------------------------------------------
// change a string to lower case, (a replacement for Borland string::to_lower())
string& tolower(string& s)
{
uint i;
for(i = 0 ; i < s.length() ; i++)
s[i] = (char)tolower(s[i]);
return(s);
} // tolower
//----------------------------------------------------------------------------
// prepend a string into another string
string& prepend(string& s, string& prefix)
{
s.insert(0,prefix);
return(s);
} // prepend
//----------------------------------------------------------------------------
// change a string to "title case", all lower case except first letter of each word
string& titlecase(string& s)
{
uint i;
for(i = 0 ; i < s.length() ; i++) // start with all lower case, was s.to_lower()
s[i] = (char)tolower(s[i]);
bool spacebefore = true; // whether the previous char was space
for(i = 0 ; i < s.length() ; i++)
{
if(spacebefore)
s[i] = (char)toupper(s[i]);
spacebefore = !isalnum(s[i]);
}
return(s);
} // titlecase
//----------------------------------------------------------------------------
// for one-step output of a string in quotes: cout << qstring(s). source string is unchanged.
string qstring(const string& s)
{
string t(s);
t += ("\"");
t.insert(0,"\"");
return(t);
} // qstring
//----------------------------------------------------------------------------
// remove the quotes from around a quoted string. source string is unchanged.
string unqstring(const string& s)
{
string t(s);
if((t.length() >= 2) && (t[0] == '\"') && (t[t.length() - 1] == '\"'))
{
t.erase(0,1);
t.erase(t.length() - 1,1);
}
return(t);
} // unqstring
//----------------------------------------------------------------------------
// for one-step output of a string in brackets: cout << bstring(s). source string is unchanged.
string bstring(const string& s)
{
string t(s);
t += ("]");
t.insert(0,"[");
return(t);
} // bstring
//----------------------------------------------------------------------------
// appends the entire contents of a file to a string.
// replacement for Borland string::read_file(istream&) with some differences.
// it does not test for NULL string terminator within the file.
string& stringfromfile(string& s, ifstream& is)
{
char ch;
while(is)
{
ch = is.get();
if(is) // supposed to prevent appending the EOF when it occurs
if(s.length() < s.max_size()) // prevent overflow
s += (char)ch;
}
return(s);
} // toupper
//----------------------------------------------------------------------------
/* fromstring 1-1-99
A strings-only override of my template version. Necessary because if this situation
fromstring(string,string) were in template version, the compiler, when instantiating the template,
would have to GENERATE the code for the line (below) "var = value" for ALL instantiations,
including those where that line is inapplicable because the types are not the same.
But if the types aren't the same, the code itself is illegal, a compile-time error.
(and there's no RTTI at compile time, so you can't conditionally exclude the code)
(am I sure there's not? Even if there is, probably not for templates.)
copies ALL of a multi-word string, which the >> procedure in template version would not.
*/
string& fromstring(string& var, const string& value)
{
var = value; // just copy the whole string
return(var);
} // fromstring
//----------------------------------------------------------------------------
/* measures how similar two strings, usually tokens (single words), are.
This is an experimental concept that was interesting to work on.
I think this is used in wtalk.cpp; some other programs have similarity() or
SimilarityTo() functions that explore a more generalized concept of measuring
"similarity" of two objects.
returns a value between 0 (no similarity at all) and 1.0 (identical)
Measure is the same regardless of which string is first or second.
Some similar and likely better measures have been devised:
See "Levenshtein distance", php functions levenshtein())and similar_text()
*/
double similarity(const string& first, const string& second)
{
string shorter = first, longer = second; // copy to local for use
if(!shorter.length() && !longer.length()) // 2 empties are equal.
return(1.0);
if(!shorter.length() || !longer.length()) // now require both to have length
return(0.0);
if(shorter.length() > longer.length()) // comparisons easier if we know which is which
swap(shorter,longer);
// calculates return value as a percentage of longer, so a short word that "contains" shorter
// is a better match than a longer word that also does. if they're equal, score = 1.0
double denominator = longer.length(); // save now; processing may destroy the string
//----------
// Version 3. NEWER version OF version 1! More restrictive.
// find what chars they share at the exact same locs. no displacements allowed for.
// you don't want "UNfastened" to match "fastened", which version 1 would do.
// e.g. run and ran match 2 chars in order, = 2/3 = .67 match.
uint count = 0; // count of matching chars
for(uint i = 0 ; i < shorter.length() ; i++)
if(shorter[i] == longer[i])
count++;
return(count / denominator);
//----------
#if 0
//----------
// Version 2. Still not convinced I've found a satisfactory one.
// newest version. It destroys the strings as it finds matches.
// it tends to overrate the degree of match.
uint totmatches = 0; // total matching chars found
while(shorter.length() && longer.length())
{
// in each string, delete all chars that don't occur in the other.
// those chars can't possibly match. Do it each loop pass because a char can
// become newly unmatched when one string has more occurrences of it than the other.
size_t u;
while((u = shorter.find_first_not_of(longer)) != string::npos) shorter.erase(u,1);
while((u = longer.find_first_not_of(shorter)) != string::npos) longer.erase(u,1);
// if either was emptied, further matches are impossible
if(!shorter.length() || !longer.length())
break;
if(shorter.length() > longer.length()) // test again
swap(shorter,longer);
uint matches = 0; // new char matches found during this loop pass
// find, tally, and delete any sequential leading matching chars (prefixes)
while(shorter.length() && (shorter[0] == longer[0]))
{
matches++;
shorter.erase(0,1);
longer.erase(0,1);
}
// start from the other end and work backwards.
// This will find matching suffixes in strings of different length.
int i = shorter.length() - 1;
int j = longer.length() - 1;
for( ; shorter.length() && (shorter[i] == longer[j]) ; i--, j--)
{
matches++;
shorter.erase(i,1);
longer.erase(j,1);
}
if(matches) // if new matches were found
totmatches += matches;
else // if no new matches, MUST prevent endless loop
break; // in case strings still have contents.
} //while(shorter.length() && longer.length())
// experimental final test (from below), untested even on paper.
// strings may have substantial internal matches that still haven't been caught.
// (such as when they're displaced by one loc). I don't do this inside loop
// because doing it multiple times looks like it can degenerate into "matching
// chars regardless of order", which must be avoided.
// test yet again
size_t u;
while((u = shorter.find_first_not_of(longer)) != string::npos) shorter.erase(u,1);
while((u = longer.find_first_not_of(shorter)) != string::npos) longer.erase(u,1);
if(shorter.length() > longer.length())
swap(shorter,longer);
uint highcount = 0; // longest sequence of matching chars
uint remaining = shorter.length(); // chars left to search in shorter
for(uint i = 0 ; (i < shorter.length()) && (highcount < remaining) ; i++, remaining--)
{
uint j = longer.find(shorter[i]);
if(j != string::npos) // if first char found, search subsequent chars
{
uint count = 0;
for(uint k = i ; (k < shorter.length()) && (j < longer.length()) ; k++, j++)
if(shorter[k] == longer[j])
count++;
highcount = max(highcount,count);
}
}
totmatches += highcount;
return(totmatches / denominator);
//----------
// version 1. seems to work better than Version 2.
// find what chars they share in the same order.
// Start a new search at EACH position in shorter, and use the highest count found.
// the first nonmatching char does not abort the search. internal nonmatches are allowed.
// #error DOES THIS ACCURATELY DESCRIBE WHAT'S BELOW? NO.
// e.g. run and ran match 2 chars in order, = 2/3 = .67 match.
// You could also factor in a measure of the displacements of the shared chars
// (i.e. they match, but are not at exactly the same locs in the strings), using a method
// similar to that for the Q-set deck, but it doesn't seem that useful or necessary.
uint highcount = 0; // longest (maybe broken) sequence of matching chars
uint remaining = shorter.length(); // chars left to search in shorter
// for speed, if there's not enough string left to search to improve highcount, quit looking.
for(uint i = 0 ; (i < shorter.length()) && (highcount < remaining) ; i++, remaining--)
{
uint j = longer.find(shorter[i]);
if(j != string::npos) // if first char found, search subsequent chars
{
// a test here similar to "remaining", above, is more trouble than it's worth.
uint count = 0;
for(uint k = i ; (k < shorter.length()) && (j < longer.length()) ; k++, j++)
if(shorter[k] == longer[j])
count++;
highcount = max(highcount,count);
}
}
return(highcount / denominator);
//----------
// in oldest version, this was the final step.
// This section is useless, *especially* for strings. Kept only for the method.
// find what percent of the chars in shorter are found in longer, regardless of order.
// this test destroys longer
highcount = 0;
for(i = 0 ; i < shorter.length() ; i++)
{
uint j = longer.find(shorter[i]);
if(j != string::npos) // char was found
{
highcount++; // increment count
longer.erase(j,1); // remove the char so it doesn't get found again
}
}
double anyorder = (double)highcount / (double)shorter.length() * .25;
#endif 0
} //similarity
//----------------------------------------------------------------------------
// returns string with each LF changed to CR. source string is unchanged.
string LFtoCR(const string& s)
{
string t;
for(uint i = 0 ; i < s.length() ; i++)
if(s[i] == '\n')
t += '\r';
else
t += s[i];
return t;
} //LFtoCR
//----------------------------------------------------------------------------
// returns string with each CR changed to LF. source string is unchanged.
// useful for making text from a TEdit suitable for writing to disk in text mode,
// (will prevent writing double-CRs).
// #error (where, if anywhere, do I use this? It would change CRLF to LFLF.)
string CRtoLF(const string& s)
{
string t;
for(uint i = 0 ; i < s.length() ; i++)
if(s[i] == '\r')
t += '\n';
else
t += s[i];
return t;
} //CRtoLF
//----------------------------------------------------------------------------
// returns string with each LF changed to CRLF. source string is unchanged.
// useful for output to modem or to a TEdit. (See mylib.cpp)
string LFtoCRLF(const string& s)
{
string t;
for(uint i = 0 ; (i < s.length()) && (t.length() < (t.max_size() - 2)) ; i++)
{
if(s[i] == '\n')
t += '\r'; // insert CR before LF
t += s[i];
}
return t;
} //LFtoCRLF
//----------------------------------------------------------------------------
// returns string with each CRLF changed to LF. source string is unchanged.
string CRLFtoLF(const string& s)
{
string t(s);
size_t u;
while((u = t.find("\r\n")) != string::npos) // find each CRLF
t.erase(u,1); // only remove each CR
return t;
} //CRLFtoLF
//----------------------------------------------------------------------------
/* STRing AND 12-2-96
An "AND" operator for strings.
Searches a given string (such as "ABCDE") for a pattern given by another
string (such as "?B??E", where the question marks are "don't care").
The above example would return true, because the target string does
contain a B in the 2nd position, and E in the 5th.
Returns true if pattern matched, false if not.
Used in CLASSIFY.CPP. Another planned use for this is in LIFE.C,
for matching strings that encode the neighboring colors around a pixel.
See paper file A-LIFE in file cabinet.
Briefly tested, seems to work ok.
string to search, pattern to search for, the dont-care char in pattern.
*/
bool strand(const char *searchin, const char *pattern, char dontcare)
{
if(strlen(pattern) > strlen(searchin)) // can't find pattern in a shorter string
return(false);
int slen = strlen(pattern);
for(int i = 0 ; i < slen ; i++)
if(pattern[i] != dontcare)
if(pattern[i] != searchin[i])
return(false);
return(true);
}
//----------------------------------------------------------------------------
// version for strings
bool strand(const string& searchin, const string& pattern, char dontcare)
{
if(pattern.length() > searchin.length()) // can't find pattern in a shorter string
return(false);
int slen = pattern.length();
for(int i = 0 ; i < slen ; i++)
if(pattern[i] != dontcare)
if(pattern[i] != searchin[i])
return(false);
return(true);
}
//----------------------------------------------------------------------------
/* strips leading and trailing whitespace from a string.
modify as needed to strip leading, trailing, both.
returns a reference to the stripped source string.
*/
string& stripstring(string& s)
{
while(s.length()) // strip leading
if(isspace(s[0]) || iscntrl(s[0]))
s.erase(0,1);
else
break;
while(s.length()) // strip trailing
if(isspace(s[s.length() - 1]) || iscntrl(s[s.length() - 1]))
s.erase(s.length() - 1);
else
break;
return(s);
}
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
/* Token extraction functions and sentence extraction functions.
A token is a punctuation mark or an alphanumeric string, which can contain
hyphens or apostrophes. This is DIFFERENT from what string::read_token() extracts.
*/
//----------------------------------------------------------------------------
/* get token from a string
this has a minor unfixable bug: if the source string ends with a "-" (such as might
happen when reading lines from a file), it means the token continues on the next line,
but this function must quit looking because the source string is used up.
returns the index of the location in the provided string that is 1 char beyond
all skipped chars and the gotten token (might be beyond end of string, too).
or 0 if no token was created. If it returns 0, it also means there is nothing
left to get. You can use: while(gettoken()){...};
sentence to scan, start, token holder
*/
uint gettoken(const string& s, unsigned startpos, string& buf)
{
buf.clear(); // must empty each call: it's a reference, and we're using +=
if(startpos >= s.length()) // startpos invalid index
return(0); // means string is used up
size_t i = startpos; // pointer into s
while(iscntrl(s[i]) || (s[i] == ' ')) // skip over control-chars (incl. nulls) & spaces
if(++i >= s.length()) // string ended
return(0); // before any valid token-starting char found
// got to here means there is a token to be got
if(ispunct(s[i])) // punctuation
{ // identical multiples, like --, allowed. Also gets "" as 1 token
string t = "?!"; // allows !? ?!?!?! ???! etc. as a single token.
char ch = s[i];
while((s[i] == ch) || ((t.find(s[i]) != string::npos) && (t.find(ch) != string::npos)) )
{
buf += s[i];
if(++i >= s.length()) // hit end of string
break;
}
}
else // alphanumeric, and non-leading hyphens, apostrophes all legal
while(isalnum(s[i]) || (s[i] == '-') || (s[i] == '\''))
{
buf += s[i];
if(++i >= s.length()) // hit end of string
break;
}
return(i); // return new search-start loc.
} // end gettoken
//----------------------------------------------------------------------------
/* get token from a stream
returns number of chars in the retrieved token. if 0, there is also nothing more to get.
You can use: while(gettoken()){...};
*/
uint gettoken(istream& is, string& buf) // stream to read from, token holder
{
buf.clear(); // must empty each call: it's a reference, and we're using +=
char ch = ' ';
while(iscntrl(ch) || (ch == ' ')) // skip over control-chars (incl. nulls) & spaces
if(!is.get(ch)) // stream used up
return(0); // before valid token-starting char found
// got to here means there is a token to be got
if(ispunct(ch)) // punctuation
{ // identical multiples, like --, allowed)
string t = "?!"; // allows !? ?!?!?! ???! etc. as a single token.
char p;
buf += ch;
// peek() can only return a value that will fit into a char.
// you should decide schar vs. uchar, but probably makes no difference.
while(is.good() && (((p = (char)(is.peek())) == ch) ||
((t.find(p) != string::npos) && (t.find(ch) != string::npos))) )
{
is.get(ch);
buf += ch;
}
}
else // alphanumeric, and non-leading hyphens, apostrophes all legal
{
buf += ch;
int test = is.peek();
while(is.good() && (isalnum(test) || (test == '-') || (test == '\'')))
{
is.get(ch);
buf += ch;
if(ch == '-') // if the char is a hyphen, extract and discard
is >> ws; // trailing or line-ending whitespace, if any
test = is.peek();
}
}
return(buf.length());
} // gettoken
//----------------------------------------------------------------------------
// returns true if token is a type that terminates a sentence
bool isterminator(const string& s)
{
if( (s == ".") || (s == ";") ||
// (s == ":") || // unsure about ":". for now, it's not a term.
(s.find("...") != string::npos) || (s.find("?") != string::npos) || (s.find("!") != string::npos) )
return(true);
return(false);
} //isterminator
//----------------------------------------------------------------------------
/* get a sentence from a string
returns the index of the location in the provided string that is 1 char beyond
all skipped chars and the gotten sentence (might be beyond end of string, too).
or 0 if no sentence was created. 0 also means there is nothing left to get.
You can use: while(getsentence()){...};
text block to scan, starting position, sentence holder
*/
uint getsentence(const string& source, uint startpos, string& sentence)
{
sentence.clear(); // always empty it at start
if(startpos >= source.length()) // source used up?
return(0);
string token;
while((startpos = gettoken(source,startpos,token)) != 0)
{
sentence += token + " ";
if(isterminator(token)) // end of sentence?
break;
}
if(sentence.length() && !isterminator(token)) // if it has contents, and no terminator,
sentence += "."; // give it one.
stripstring(sentence); // new fn, see above.
// if gettoken failed, we might first have gotten a sentence before it did.
// so THIS function shouldn't report failure until the next call.
if(!startpos && sentence.length())
startpos = source.length(); // set startpos so next call fails
return(startpos);
} //getsentence
//----------------------------------------------------------------------------
/* extract a sentence from a stream.
returns number of chars in the retrieved sentence. if 0, there is also nothing more to get.
You can use: while(getsentence()){...};
*/
uint getsentence(istream& is, string& sentence)
{
sentence.clear(); // always empty it at start
string token;
while(gettoken(is,token))
{
sentence += token + " ";
if(isterminator(token)) // end of sentence?
break;
}
if(sentence.length() && !isterminator(token)) // if it has contents, and no terminator,
sentence += "."; // give it one.
stripstring(sentence);
return(sentence.length());
} //getsentence
//----------------------------------------------------------------------------
// Translate Control Chars fns
//----------------------------------------------------------------------------
// replaces control chars in a string with their corresponding ASCII mnemonics.
// CRLF turns into the STRING "<CR><LF>", etc.
// returns a string containing the translation; source string s is unchanged.
string TranslateControls(const string& s)
{
static char* ControlText[32] =
{
"<NUL>","<SOH>","<STX>","<ETX>","<EOT>","<ENQ>","<ACK>","<BEL>","<BS>","<HT>",
"<LF>","<VT>","<FF>","<CR>","<SO>","<SI>","<DLE>","<DCl>","<DC2>","<DC3>",
"<DC4>","<NAK>","<SYN>","<ETB>","<CAN>","<EM>","<SUB>","<ESC>","<FS>","<GS>",
"<RS>","<US>"
};
string t;
int ch;
for(size_t i = 0 ; i < s.length() ; i++)
{
ch = s[i] & 0xFF; // mapdown into ASCII range
if(ch < 32)
t += ControlText[ch]; // control code
else
if(ch == 127) // special, DEL is out of normal sequence
t += "<DEL>";
else
t += s[i]; // normal char (retains original parity bit, if set)
}
return(t);
} //TranslateControls
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
// TRIG MATH FUNCTIONS
//----------------------------------------------------------------------------
double cosine(double degrees) { return(cos(degrees * M_PI_180)); } // radians = degrees * pi/180
double sine(double degrees) { return(sin(degrees * M_PI_180)); } // radians = degrees * pi/180
//----------------------------------------------------------------------------
// whole-degrees sine/cosine tables
// constructor
Trigtable::Trigtable()
{
for(int i = 0 ; i < 360 ; i++)
{
cosines[i] = cosine(i);
sines[i] = sine(i);
}
// set some exact entries to avoid rounding errors.
cosines[90] = cosines[270] = sines[0] = sines[180] = 0;
cosines[0] = sines[90] = 1;
cosines[180] = sines[270] = -1;
sines[30] = sines[150] = cosines[60] = cosines[300] = .5;
sines[330] = sines[210] = cosines[120] = cosines[240] = -.5;
}
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
// Some console utility fns.
//----------------------------------------------------------------------------
/* says press any key to continue, and waits for a key. erases its message
to leave a cleaner screen, but moves to new line anyway so that
if echoed to printer with ^P, subsequent text won't overwrite.
The default os is cout, but can be, for example, a constream.
12-2-99 returns the char you pressed so it can be used.
*/
int presskey(ostream& os)
{
os << "Press any key to continue...";
int ch = _getch();
os << "\r " << endl;
return(ch);
}
//----------------------------------------------------------------------------
/* prints a specified error message and aborts program execution
"plain" version. see graborts() for version that calls closegraph().
Advantage of this over BC4 abort() is that this will cause destructors
to be called for all objects, while abort() calls NO destructors, not
even for globals (PG:147). Any open fstreams are closed by their destructors.
On the other hand, calling destructors in a crashing program may not be good. Oh well.
*/
void aborts(const string& s)
{
cerr << endl << s << endl;
presskey(); // prevent msg scrolling off before being seen
exit(1);
}
//----------------------------------------------------------------------------
/* Asks user a yes/no question, using either cout or a provided constream.
returns true if the answer is yes, false if no.
prompt is a string, not a string&, so it can have a default value (see my.h)
(Figure out and document somewhere why I decided a reference parm couldn't be given
a default value.)
*/
bool yesno(string prompt, ostream& os)
{
int ch;
os << endl;
do
{
os << prompt << " (y/n)? ";
ch = tolower(_getche());
os << endl;
}
while((ch != 'y') && (ch != 'n'));
return(ch == 'y');
}
//----------------------------------------------------------------------------
// Untested.
//int FontHeightInPixels(int pointsize)
//{
// TScreenDC dc;
// return((long)(-pointsize) * (long)(dc.GetDeviceCaps(LOGPIXELSY)) / 72L);
//}
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
|
|
|
|