diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index 6865e6a8..fde1812f 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -238,6 +238,35 @@ namespace simgear { return do_strip( s, BOTHSTRIP ); } + void + stripTrailingNewlines_inplace(string& s) + { + // The following (harder to read) implementation is much slower on + // my system (g++ 6.2.1 on Debian): 11.4 vs. 3.9 seconds on + // 50,000,000 iterations performed on a short CRLF-terminated + // string---and it is even a bit slower (12.9 seconds) with + // std::next(it) instead of (it+1). + // + // for (string::reverse_iterator it = s.rbegin(); + // it != s.rend() && (*it == '\r' || *it == '\n'); /* empty */) { + // it = string::reverse_iterator(s.erase( (it+1).base() )); + // } + + // Simple and fast + while (!s.empty() && (s.back() == '\r' || s.back() == '\n')) { + s.pop_back(); + } + } + + string + stripTrailingNewlines(const string& s) + { + string res = s; + stripTrailingNewlines_inplace(res); + + return res; + } + string rpad( const string & s, string::size_type length, char c ) { diff --git a/simgear/misc/strutils.hxx b/simgear/misc/strutils.hxx index dd78863c..4343971a 100644 --- a/simgear/misc/strutils.hxx +++ b/simgear/misc/strutils.hxx @@ -70,6 +70,24 @@ namespace simgear { std::string rstrip( const std::string& s ); std::string strip( const std::string& s ); + /** + * Return a new string with any trailing \r and \n characters removed. + * Typically useful to clean a CR-terminated line obtained from + * std::getline() which, upon reading CRLF (\r\n), discards the Line + * Feed character (\n) but leaves the Carriage Return (\r) in the + * string. + * @param s Input string + * @return The cleaned string + */ + std::string stripTrailingNewlines(const std::string& s); + + /** + * Strip any trailing \r and \n characters from a string. + * Should have slightly less overhead than stripTrailingNewlines(). + * @param s Input string (modified in-place) + */ + void stripTrailingNewlines_inplace(std::string& s); + /** * Right-padding of a string to a given length * @param s String to pad diff --git a/simgear/misc/strutils_test.cxx b/simgear/misc/strutils_test.cxx index b62ad37c..e0fdbdee 100644 --- a/simgear/misc/strutils_test.cxx +++ b/simgear/misc/strutils_test.cxx @@ -25,6 +25,34 @@ void test_strip() SG_CHECK_EQUAL(strutils::strip("\t \na \t b\r \n "), "a \t b"); } +void test_stripTrailingNewlines() +{ + SG_CHECK_EQUAL(strutils::stripTrailingNewlines("\rfoobar\n\r\n\n\r\r"), + "\rfoobar"); + SG_CHECK_EQUAL(strutils::stripTrailingNewlines("\rfoobar\r"), "\rfoobar"); + SG_CHECK_EQUAL(strutils::stripTrailingNewlines("\rfoobar"), "\rfoobar"); + SG_CHECK_EQUAL(strutils::stripTrailingNewlines(""), ""); +} + +void test_stripTrailingNewlines_inplace() +{ + string s = "\r\n\r\rfoo\n\r\rbar\n\r\n\r\r\n\r\r"; + strutils::stripTrailingNewlines_inplace(s); + SG_CHECK_EQUAL(s, "\r\n\r\rfoo\n\r\rbar"); + + s = "\rfoobar\r"; + strutils::stripTrailingNewlines_inplace(s); + SG_CHECK_EQUAL(s, "\rfoobar"); + + s = "\rfoobar"; + strutils::stripTrailingNewlines_inplace(s); + SG_CHECK_EQUAL(s, "\rfoobar"); + + s = ""; + strutils::stripTrailingNewlines_inplace(s); + SG_CHECK_EQUAL(s, ""); +} + void test_starts_with() { SG_VERIFY(strutils::starts_with("banana", "ban")); @@ -148,6 +176,8 @@ void test_error_string() int main(int argc, char* argv[]) { test_strip(); + test_stripTrailingNewlines(); + test_stripTrailingNewlines_inplace(); test_starts_with(); test_ends_with(); test_simplify();