From a26c677df4c997a700a2e50842f927849c05e4fe Mon Sep 17 00:00:00 2001 From: curt Date: Sat, 15 Feb 2003 18:43:59 +0000 Subject: [PATCH] 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. --- simgear/misc/Makefile.am | 7 ++- simgear/misc/tabbed_values.cxx | 75 +++++++++++++++++++++++++++++ simgear/misc/tabbed_values.hxx | 36 ++++++++++++++ simgear/misc/tabbed_values_test.cxx | 71 +++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 simgear/misc/tabbed_values.cxx create mode 100644 simgear/misc/tabbed_values.hxx create mode 100644 simgear/misc/tabbed_values_test.cxx diff --git a/simgear/misc/Makefile.am b/simgear/misc/Makefile.am index b3808f21..d27727f5 100644 --- a/simgear/misc/Makefile.am +++ b/simgear/misc/Makefile.am @@ -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) diff --git a/simgear/misc/tabbed_values.cxx b/simgear/misc/tabbed_values.cxx new file mode 100644 index 00000000..fb935e43 --- /dev/null +++ b/simgear/misc/tabbed_values.cxx @@ -0,0 +1,75 @@ +#include "tabbed_values.hxx" + +SGTabbedValues::SGTabbedValues(const char *line) : + _line(line) +{ + assert(line); + _fields.push_back(const_cast(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(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); +} diff --git a/simgear/misc/tabbed_values.hxx b/simgear/misc/tabbed_values.hxx new file mode 100644 index 00000000..895e62b8 --- /dev/null +++ b/simgear/misc/tabbed_values.hxx @@ -0,0 +1,36 @@ +#ifndef SG_TABBED_VALUES_HXX +#define SG_TABBED_VALUES_HXX + +#include +#include + +#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 _fields; +}; + +#endif diff --git a/simgear/misc/tabbed_values_test.cxx b/simgear/misc/tabbed_values_test.cxx new file mode 100644 index 00000000..0a5dfee0 --- /dev/null +++ b/simgear/misc/tabbed_values_test.cxx @@ -0,0 +1,71 @@ +//////////////////////////////////////////////////////////////////////// +// Test harness. +//////////////////////////////////////////////////////////////////////// + +#include + +#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; +} -- 2.39.5