]> git.mxchange.org Git - simgear.git/commitdiff
James Turner:
authorcurt <curt>
Sat, 15 Feb 2003 18:43:59 +0000 (18:43 +0000)
committercurt <curt>
Sat, 15 Feb 2003 18:43:59 +0000 (18:43 +0000)
- 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
simgear/misc/tabbed_values.cxx [new file with mode: 0644]
simgear/misc/tabbed_values.hxx [new file with mode: 0644]
simgear/misc/tabbed_values_test.cxx [new file with mode: 0644]

index b3808f2152cc90081080ee5707c29a6e9d786d64..d27727f560ec9b45a45657c8558f3fd34310a7ec 100644 (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)
diff --git a/simgear/misc/tabbed_values.cxx b/simgear/misc/tabbed_values.cxx
new file mode 100644 (file)
index 0000000..fb935e4
--- /dev/null
@@ -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);
+}
diff --git a/simgear/misc/tabbed_values.hxx b/simgear/misc/tabbed_values.hxx
new file mode 100644 (file)
index 0000000..895e62b
--- /dev/null
@@ -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
diff --git a/simgear/misc/tabbed_values_test.cxx b/simgear/misc/tabbed_values_test.cxx
new file mode 100644 (file)
index 0000000..0a5dfee
--- /dev/null
@@ -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;
+}