string: add tokeniser
This commit is contained in:
parent
28d44593d8
commit
6a1ea8ff29
@ -399,6 +399,7 @@ TEST_BIN = \
|
|||||||
test/sha1 \
|
test/sha1 \
|
||||||
test/sha2 \
|
test/sha2 \
|
||||||
test/signal \
|
test/signal \
|
||||||
|
test/string \
|
||||||
test/stringid \
|
test/stringid \
|
||||||
test/strongdef \
|
test/strongdef \
|
||||||
test/tuple \
|
test/tuple \
|
||||||
|
93
string.cpp
93
string.cpp
@ -11,15 +11,16 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
*
|
||||||
* Copyright 2011 Danny Robson <danny@nerdcruft.net>
|
* Copyright 2011-2016 Danny Robson <danny@nerdcruft.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "./string.hpp"
|
#include "./string.hpp"
|
||||||
|
|
||||||
#include "./cast.hpp"
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
using util::tokeniser;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// TODO: Horribly inefficient, but God help you if you're relying on this
|
// TODO: Horribly inefficient, but God help you if you're relying on this
|
||||||
// being efficient in the first place.
|
// being efficient in the first place.
|
||||||
bool
|
bool
|
||||||
@ -27,3 +28,89 @@ strbegins (const char *restrict str,
|
|||||||
const char *restrict prefix) {
|
const char *restrict prefix) {
|
||||||
return 0 == strncmp (prefix, str, strlen (prefix));
|
return 0 == strncmp (prefix, str, strlen (prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
tokeniser::tokeniser (const string_type &_value,
|
||||||
|
value_type _separator):
|
||||||
|
m_value (_value),
|
||||||
|
m_separator (_separator)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
tokeniser::iterator
|
||||||
|
tokeniser::cbegin (void) const
|
||||||
|
{
|
||||||
|
return iterator (
|
||||||
|
{m_value.cbegin (), m_value.cend ()},
|
||||||
|
m_separator
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
tokeniser::iterator
|
||||||
|
tokeniser::cend (void) const
|
||||||
|
{
|
||||||
|
return iterator (
|
||||||
|
{m_value.cend (), m_value.cend ()},
|
||||||
|
m_separator
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
tokeniser::iterator::iterator (range_type _range, value_type _separator):
|
||||||
|
m_separator (_separator),
|
||||||
|
m_range (_range.cbegin (),
|
||||||
|
std::find (_range.cbegin (),
|
||||||
|
_range.cend (),
|
||||||
|
_separator)),
|
||||||
|
m_end (_range.cend ())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
tokeniser::iterator
|
||||||
|
tokeniser::iterator::operator++ (int)
|
||||||
|
{
|
||||||
|
iterator res(*this);
|
||||||
|
++*this;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
tokeniser::iterator&
|
||||||
|
tokeniser::iterator::operator++ (void)
|
||||||
|
{
|
||||||
|
auto newend = m_range.cend ();
|
||||||
|
if (newend != m_end) {
|
||||||
|
CHECK_EQ (*m_range.cend (), m_separator);
|
||||||
|
newend++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_range = {
|
||||||
|
newend,
|
||||||
|
std::find (newend, m_end, m_separator)
|
||||||
|
};
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
tokeniser::iterator::range_type
|
||||||
|
tokeniser::iterator::operator* (void) const
|
||||||
|
{
|
||||||
|
return m_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool
|
||||||
|
tokeniser::iterator::operator== (const iterator &rhs) const
|
||||||
|
{
|
||||||
|
return m_range == rhs.m_range && m_separator == rhs.m_separator;
|
||||||
|
}
|
||||||
|
46
string.hpp
46
string.hpp
@ -11,15 +11,59 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
*
|
||||||
* Copyright 2011 Danny Robson <danny@nerdcruft.net>
|
* Copyright 2011-2016 Danny Robson <danny@nerdcruft.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __UTIL_STRING_HPP
|
#ifndef __UTIL_STRING_HPP
|
||||||
#define __UTIL_STRING_HPP
|
#define __UTIL_STRING_HPP
|
||||||
|
|
||||||
|
#include "./view.hpp"
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
strbegins(const char *restrict str,
|
strbegins(const char *restrict str,
|
||||||
const char *restrict prefix);
|
const char *restrict prefix);
|
||||||
|
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
struct tokeniser {
|
||||||
|
public:
|
||||||
|
using string_type = std::string;
|
||||||
|
using value_type = string_type::value_type;
|
||||||
|
|
||||||
|
tokeniser (const std::string &value, char separator);
|
||||||
|
tokeniser (std::string &&value, char separator) = delete;
|
||||||
|
|
||||||
|
struct iterator {
|
||||||
|
public:
|
||||||
|
using range_type = util::view<string_type::const_iterator>;
|
||||||
|
|
||||||
|
iterator (range_type range, char separator);
|
||||||
|
|
||||||
|
iterator operator++ (int);
|
||||||
|
iterator& operator++ (void);
|
||||||
|
|
||||||
|
range_type operator* (void) const;
|
||||||
|
|
||||||
|
bool operator== (const iterator&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const value_type m_separator;
|
||||||
|
range_type m_range;
|
||||||
|
string_type::const_iterator m_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator cbegin (void) const;
|
||||||
|
iterator begin (void) const;
|
||||||
|
|
||||||
|
iterator cend (void) const;
|
||||||
|
iterator end (void) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const string_type &m_value;
|
||||||
|
const value_type m_separator;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#endif // __UTIL_STRING_HPP
|
#endif // __UTIL_STRING_HPP
|
||||||
|
|
||||||
|
35
test/string.cpp
Normal file
35
test/string.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include <cruft/util/tap.hpp>
|
||||||
|
#include <cruft/util/string.hpp>
|
||||||
|
#include <cruft/util/types.hpp>
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int, char**)
|
||||||
|
{
|
||||||
|
util::TAP::logger tap;
|
||||||
|
|
||||||
|
const char csv[] = "\0,a,123,,this is a test,";
|
||||||
|
const std::string values[] = {
|
||||||
|
{ "\0", 1 },
|
||||||
|
{ "a" },
|
||||||
|
{ "123" },
|
||||||
|
{ "" },
|
||||||
|
{ "this is a test" },
|
||||||
|
{ "" }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string str (std::cbegin (csv), std::cbegin (csv) + elems (csv));
|
||||||
|
auto tok = util::tokeniser (str, ',');
|
||||||
|
auto t_cursor = tok.cbegin ();
|
||||||
|
auto v_cursor = std::cbegin (values);
|
||||||
|
|
||||||
|
tap.expect_eq (*t_cursor++, *v_cursor++, "tokeniser, single letter");
|
||||||
|
tap.expect_eq (*t_cursor++, *v_cursor++, "tokeniser, three digits");
|
||||||
|
tap.expect_eq (*t_cursor++, *v_cursor++, "tokeniser, embedded null");
|
||||||
|
tap.expect_eq (*t_cursor++, *v_cursor++, "tokeniser, empty string");
|
||||||
|
tap.expect_eq (*t_cursor++, *v_cursor++, "tokeniser, string with spaces");
|
||||||
|
tap.expect_eq (*t_cursor++, *v_cursor++, "tokeniser, trailing empty");
|
||||||
|
|
||||||
|
tap.expect_eq (t_cursor, tok.cend (), "tokeniser iterator at end");
|
||||||
|
|
||||||
|
return tap.status ();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user