sgstream.hxx \
stopwatch.hxx \
strutils.hxx \
+ tabbed_values.hxx \
texcoord.hxx \
zfstream.hxx
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)
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+////////////////////////////////////////////////////////////////////////
+// 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;
+}