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:
@@ -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)
|
||||
|
||||
75
simgear/misc/tabbed_values.cxx
Normal file
75
simgear/misc/tabbed_values.cxx
Normal 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);
|
||||
}
|
||||
36
simgear/misc/tabbed_values.hxx
Normal file
36
simgear/misc/tabbed_values.hxx
Normal 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
|
||||
71
simgear/misc/tabbed_values_test.cxx
Normal file
71
simgear/misc/tabbed_values_test.cxx
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user