James Turner:

- added a new class in simgear/misc, SGTabbedValues, which parses a
  null-terminated string of data separated by tabs into fields, and
  supports safe conversion into various other datatypes.
This commit is contained in:
curt
2003-02-15 18:43:59 +00:00
parent 5d10dda5a1
commit a26c677df4
4 changed files with 188 additions and 1 deletions

View File

@@ -11,6 +11,7 @@ include_HEADERS = \
sgstream.hxx \
stopwatch.hxx \
strutils.hxx \
tabbed_values.hxx \
texcoord.hxx \
zfstream.hxx
@@ -22,12 +23,16 @@ libsgmisc_a_SOURCES = \
sg_path.cxx \
sgstream.cxx \
strutils.cxx \
tabbed_values.cxx \
texcoord.cxx \
zfstream.cxx
noinst_PROGRAMS = props_test
noinst_PROGRAMS = props_test tabbed_value_test
props_test_SOURCES = props_test.cxx
props_test_LDADD = libsgmisc.a ../xml/libsgxml.a ../debug/libsgdebug.a
tabbed_value_test_SOURCES = tabbed_values_test.cxx
tabbed_value_test_LDADD = libsgmisc.a ../debug/libsgdebug.a
INCLUDES = -I$(top_srcdir)

View File

@@ -0,0 +1,75 @@
#include "tabbed_values.hxx"
SGTabbedValues::SGTabbedValues(const char *line) :
_line(line)
{
assert(line);
_fields.push_back(const_cast<char*>(line));
}
const char* SGTabbedValues::fieldAt(const unsigned int index) const
{
// we already computed that offset, cool
if (_fields.size() > index)
return _fields[index];
while (_fields.size() <= index) {
char* nextField = _fields.back();
if (*nextField=='\0') return NULL; // we went off the end
while (*nextField != '\t') {
if (*nextField=='\0') return NULL; // we went off the end
++nextField;
}
_fields.push_back(++nextField);
}
return _fields.back();
}
string SGTabbedValues::operator[](const unsigned int offset) const
{
const char *data = fieldAt(offset);
char* endPtr = const_cast<char*>(data);
int len = 0;
while ((*endPtr != '\0') && (*endPtr != '\t')) {
++len;
++endPtr;
}
return string(fieldAt(offset), len);
}
bool SGTabbedValues::isValueAt(const unsigned int offset) const
{
const char *data = fieldAt(offset);
return data && (*data != '\t'); // must be non-NULL and non-tab
}
char SGTabbedValues::getCharAt(const unsigned int offset) const
{
const char *data = fieldAt(offset);
if (!data || (*data == '\t'))
return 0;
return *data;
}
double SGTabbedValues::getDoubleAt(const unsigned int offset) const
{
const char *data = fieldAt(offset);
if (!data || (*data == '\t'))
return 0;
/* this is safe because strtod will stop parsing when it sees an unrecogznied
character, which includes tab. */
return strtod(data, NULL);
}
long SGTabbedValues::getLongAt(const unsigned int offset) const
{
const char *data = fieldAt(offset);
if (!data || (*data == '\t'))
return 0;
return strtol(data, NULL, 0);
}

View File

@@ -0,0 +1,36 @@
#ifndef SG_TABBED_VALUES_HXX
#define SG_TABBED_VALUES_HXX
#include <vector>
#include <string>
#include "simgear/compiler.h"
SG_USING_STD(vector);
SG_USING_STD(string);
class SGTabbedValues
{
public:
SGTabbedValues(const char* line);
string operator[](const unsigned int) const;
bool isValueAt(const unsigned int) const;
double getDoubleAt(const unsigned int) const;
char getCharAt(const unsigned int) const;
long getLongAt(const unsigned int) const;
private:
const char* fieldAt(const unsigned int offset) const;
const char* _line;
/** this is first character of each field, if the field is empty
it will be the tab character. It is lazily built as needed, so
if only the first field is accessed (which is a common case) we
don't iterative over the whole line. */
mutable vector<char*> _fields;
};
#endif

View File

@@ -0,0 +1,71 @@
////////////////////////////////////////////////////////////////////////
// Test harness.
////////////////////////////////////////////////////////////////////////
#include <simgear/compiler.h>
#include STL_IOSTREAM
#include "tabbed_values.hxx"
SG_USING_STD(cout);
SG_USING_STD(cerr);
SG_USING_STD(endl);
int main (int ac, char ** av)
{
const char* string1 = "Hello\tWorld\t34\tZ\t\tThere Is No Spoon";
SGTabbedValues tv(string1);
if (tv[0] != "Hello") {
cerr << "failed to read string at index 0" << endl;
return 1;
}
if (tv[1] != "World") {
cerr << "failed to read string at index 1" << endl;
return 1;
}
if (tv[2] != "34") {
cerr << "failed to read string at index 2" << endl;
return 1;
}
double dval = tv.getDoubleAt(2);
if (dval != 34.0) {
cerr << "failed to read double at index 2" << endl;
return 2;
}
char cval = tv.getCharAt(3);
if (cval != 'Z') {
cerr << "failed to read char at index 3" << endl;
return 1;
}
cval = tv.getCharAt(0);
if (cval != 'H') {
cerr << "failed to read char at index 0" << endl;
return 1;
}
if (tv.isValueAt(4)) {
cerr << "didn't identify skipped value correctly" << endl;
return 3;
}
if (!tv.isValueAt(3)) {
cerr << "didn't identify present value correctly" << endl;
return 3;
}
if (tv[5] != "There Is No Spoon") {
cerr << "failed to read string at index 5 (got [" << tv[5] << "]" << endl;
return 1;
}
cout << "all tests passed successfully!" << endl;
return 0;
}