From: Vadim Zeitlin Date: Sun, 18 Mar 2007 14:43:41 +0000 (+0000) Subject: added wxJoin and wxSplit functions (modified patch 1638950) X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/abbb59e8eb995f445759df30ee507c74af690b51 added wxJoin and wxSplit functions (modified patch 1638950) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@44890 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/docs/changes.txt b/docs/changes.txt index 97fa8ebd9c..212e6e800c 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -58,6 +58,13 @@ Major new features in this release All: +- Added wxJoin() and wxSplit() functions (Francesco Montorsi) +- Added wxMutex::LockTimeout() (Aleksandr Napylov) +- Added wxMemoryInputStream(wxInputStream&) ctor (Stas Sergeev) +- Implemented wxMemoryInputStream::CanRead() + +All (GUI): + - Added wxEventBlocker class (Francesco Montorsi). - Added wxFile/DirPickerCtrl::Get/SetFile/DirName() (Francesco Montorsi). - Added wxSizerFlags::Top() and Bottom(). @@ -65,13 +72,11 @@ All: - Fixed tab-related drawing and hit-testing bugs in wxRichTextCtrl. - Implemented background colour in wxRichTextCtrl. - Fixed crashes in helpview when opening a file. -- Added wxMutex::LockTimeout() (Aleksandr Napylov) - Set locale to the default in all ports, not just wxGTK - Added wxJoystick::GetButtonState/Position() (Frank C Szczerba) - Added wxGridUpdateLocker helper class (Evgeniy Tarassov) - Support wxGRID_AUTOSIZE in wxGrid::SetRow/ColLabelSize() (Evgeniy Tarassov) -- Added wxMemoryInputStream(wxInputStream&) ctor (Stas Sergeev) -- Implemented wxMemoryInputStream::CanRead() +- Add support for tags to wxrc wxGTK: diff --git a/docs/latex/wx/function.tex b/docs/latex/wx/function.tex index b3d9a07512..8d04a686eb 100644 --- a/docs/latex/wx/function.tex +++ b/docs/latex/wx/function.tex @@ -179,6 +179,7 @@ the corresponding topic. \helpref{wxIsPlatformLittleEndian}{wxisplatformlittleendian}\\ \helpref{wxIsPlatform64Bit}{wxisplatform64bit}\\ \helpref{wxIsWild}{wxiswild}\\ +\helpref{wxJoin}{wxjoin}\\ \helpref{wxKill}{wxkill}\\ \helpref{wxLaunchDefaultBrowser}{wxlaunchdefaultbrowser}\\ \helpref{wxLEAVE\_CRIT\_SECT}{wxleavecritsect}\\ @@ -238,6 +239,7 @@ the corresponding topic. \helpref{wxShutdown}{wxshutdown}\\ \helpref{wxSleep}{wxsleep}\\ \helpref{wxSnprintf}{wxsnprintf}\\ +\helpref{wxSplit}{wxsplit}\\ \helpref{wxSplitPath}{wxsplitfunction}\\ \helpref{wxStartTimer}{wxstarttimer}\\ \helpref{wxStaticCast}{wxstaticcast}\\ @@ -694,6 +696,23 @@ application. See \helpref{wxCloseEvent}{wxcloseevent} and \helpref{wxApp}{wxapp} +\membersection{::wxJoin}\label{wxjoin} + +\func{wxString}{wxJoin}{\param{const wxArrayString\&}{ arr}, \param{const wxChar}{ sep}, \param{const wxChar}{ escape = '\\'}} + +Concatenate all lines of the given \helpref{wxArrayString}{wxarraystring} object using the separator \arg{sep} and returns +the result as a \helpref{wxString}{string}. + +If the \arg{escape} character is non-\NULL, then it's used as prefix for each occurrence of \arg{sep} +in the strings contained in \arg{arr} before joining them which is necessary +in order to be able to recover the original array contents from the string +later using \helpref{wxSplit}{wxsplit}. + +\wxheading{Include files} + + + + \membersection{::wxKill}\label{wxkill} \func{int}{wxKill}{\param{long}{ pid}, \param{int}{ sig = wxSIGTERM}, \param{wxKillError }{*rc = NULL}, \param{int }{flags = 0}} @@ -1287,6 +1306,27 @@ Sets the current working directory, returning true if the operation succeeded. Under MS Windows, the current drive is also changed if {\it dir} contains a drive specification. +\membersection{::wxSplit}\label{wxsplit} + +\func{wxArrayString}{wxSplit}{\param{const wxString\&}{ str}, \param{const wxChar}{ sep}, \param{const wxChar}{ escape = '\\'}} + +Splits the given \helpref{wxString}{wxstring} object using the separator \arg{sep} and returns the +result as a \helpref{wxArrayString}{wxarraystring}. + +If the \arg{escape} character is non-\NULL, then the occurrences of \arg{sep} immediately prefixed +with \arg{escape} are not considered as separators. + +Note that empty tokens will be generated if there are two or more adjacent separators. + +\wxheading{See also} + +\helpref{wxJoin}{wxjoin} + +\wxheading{Include files} + + + + \membersection{::wxSplitPath}\label{wxsplitfunction} \func{void}{wxSplitPath}{\param{const char *}{ fullname}, \param{wxString *}{ path}, \param{wxString *}{ name}, \param{wxString *}{ ext}} diff --git a/include/wx/arrstr.h b/include/wx/arrstr.h index 570d4672e8..ad82252b90 100644 --- a/include/wx/arrstr.h +++ b/include/wx/arrstr.h @@ -56,7 +56,7 @@ public: wxArrayStringBase::Add(string, copies); return size() - copies; } -}; +} class WXDLLIMPEXP_BASE wxSortedArrayString : public wxSortedArrayStringBase { @@ -343,4 +343,21 @@ private: wxString* m_strings; }; -#endif + +// ---------------------------------------------------------------------------- +// helper functions for working with arrays +// ---------------------------------------------------------------------------- + +// by default, these functions use the escape character to escape the +// separators occuring inside the string to be joined, this can be disabled by +// passing '\0' as escape + +WXDLLIMPEXP_BASE wxString wxJoin(const wxArrayString& arr, + const wxChar sep, + const wxChar escape = wxT('\\')); + +WXDLLIMPEXP_BASE wxArrayString wxSplit(const wxString& str, + const wxChar sep, + const wxChar escape = wxT('\\')); + +#endif // _WX_ARRSTR_H diff --git a/src/common/string.cpp b/src/common/string.cpp index 2a495e50c2..68da706c98 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -2527,3 +2527,106 @@ int wxCMPFUNC_CONV wxStringSortDescending(wxString* s1, wxString* s2) { return -s1->Cmp(*s2); } + + + +// =========================================================================== +// wxJoin and wxSplit +// =========================================================================== + +#include "wx/tokenzr.h" + +wxString wxJoin(const wxArrayString& arr, const wxChar sep, const wxChar escape) +{ + size_t count = arr.size(); + if ( count == 0 ) + return wxEmptyString; + + wxString str; + + // pre-allocate memory using the estimation of the average length of the + // strings in the given array: this is very imprecise, of course, but + // better than nothing + str.reserve(count*(arr[0].length() + arr[count-1].length()) / 2); + + if ( escape == wxT('\0') ) + { + // escaping is disabled: + for ( size_t i = 0; i < count; i++ ) + { + if ( i ) + str += sep; + str += arr[i]; + } + } + else // use escape character + { + for ( size_t n = 0; n < count; n++ ) + { + if ( n ) + str += sep; + + for ( wxString::const_iterator i = arr[n].begin(), + end = arr[n].end(); + i != end; + ++i ) + { + const wxChar ch = *i; + if ( ch == sep ) + str += escape; // escape this separator + str += ch; + } + } + } + + str.Shrink(); // release extra memory if we allocated too much + return str; +} + +wxArrayString wxSplit(const wxString& str, const wxChar sep, const wxChar escape) +{ + if ( escape == wxT('\0') ) + { + // simple case: we don't need to honour the escape character + return wxStringTokenize(str, sep, wxTOKEN_RET_EMPTY_ALL); + } + + wxArrayString ret; + wxString curr; + wxChar prev = wxT('\0'); + + for ( wxString::const_iterator i = str.begin(), + end = str.end(); + i != end; + ++i ) + { + const wxChar ch = *i; + + if ( ch == sep ) + { + if ( prev == escape ) + { + // remove the escape character and don't consider this + // occurrence of 'sep' as a real separator + *curr.rbegin() = sep; + } + else // real separator + { + ret.push_back(curr); + curr.clear(); + } + } + else // normal character + { + curr += ch; + } + + prev = ch; + } + + // add the last token + if ( !curr.empty() || prev == sep ) + ret.Add(curr); + + return ret; +} diff --git a/tests/arrays/arrays.cpp b/tests/arrays/arrays.cpp index a776a2c1db..8d05e0ca8d 100644 --- a/tests/arrays/arrays.cpp +++ b/tests/arrays/arrays.cpp @@ -151,6 +151,10 @@ public: private: CPPUNIT_TEST_SUITE( ArraysTestCase ); CPPUNIT_TEST( wxStringArrayTest ); + CPPUNIT_TEST( wxStringArraySplitTest ); + CPPUNIT_TEST( wxStringArrayJoinTest ); + CPPUNIT_TEST( wxStringArraySplitJoinTest ); + CPPUNIT_TEST( wxObjArrayTest ); CPPUNIT_TEST( wxArrayUShortTest ); CPPUNIT_TEST( wxArrayIntTest ); @@ -160,6 +164,9 @@ private: CPPUNIT_TEST_SUITE_END(); void wxStringArrayTest(); + void wxStringArraySplitTest(); + void wxStringArrayJoinTest(); + void wxStringArraySplitJoinTest(); void wxObjArrayTest(); void wxArrayUShortTest(); void wxArrayIntTest(); @@ -296,6 +303,159 @@ void ArraysTestCase::wxStringArrayTest() _T("a") ) ); } +void ArraysTestCase::wxStringArraySplitTest() +{ + // test wxSplit: + + { + wxString str = wxT(",,,,first,second,third,,"); + const wxChar *expected[] = + { wxT(""), wxT(""), wxT(""), wxT(""), wxT("first"), + wxT("second"), wxT("third"), wxT(""), wxT("") }; + + wxArrayString exparr(WXSIZEOF(expected), expected); + wxArrayString realarr(wxSplit(str, wxT(','))); + CPPUNIT_ASSERT( exparr == realarr ); + } + + { + wxString str = wxT(",\\,first,second,third,"); + const wxChar *expected[] = + { wxT(""), wxT(",first"), wxT("second"), wxT("third"), wxT("") }; + const wxChar *expected2[] = + { wxT(""), wxT("\\"), wxT("first"), wxT("second"), wxT("third"), wxT("") }; + + // escaping on: + wxArrayString exparr(WXSIZEOF(expected), expected); + wxArrayString realarr(wxSplit(str, wxT(','), wxT('\\'))); + CPPUNIT_ASSERT( exparr == realarr ); + + // escaping turned off: + wxArrayString exparr2(WXSIZEOF(expected2), expected2); + wxArrayString realarr2(wxSplit(str, wxT(','), wxT('\0'))); + CPPUNIT_ASSERT( exparr2 == realarr2 ); + } + + { + // test is escape characters placed before non-separator character are + // just ignored as they should: + wxString str = wxT(",\\,,fir\\st,se\\cond\\,,\\third"); + const wxChar *expected[] = + { wxT(""), wxT(","), wxT("fir\\st"), wxT("se\\cond,"), wxT("\\third") }; + const wxChar *expected2[] = + { wxT(""), wxT("\\"), wxT(""), wxT("fir\\st"), wxT("se\\cond\\"), + wxT(""), wxT("\\third") }; + + // escaping on: + wxArrayString exparr(WXSIZEOF(expected), expected); + wxArrayString realarr(wxSplit(str, wxT(','), wxT('\\'))); + CPPUNIT_ASSERT( exparr == realarr ); + + // escaping turned off: + wxArrayString exparr2(WXSIZEOF(expected2), expected2); + wxArrayString realarr2(wxSplit(str, wxT(','), wxT('\0'))); + CPPUNIT_ASSERT( exparr2 == realarr2 ); + } +} + +void ArraysTestCase::wxStringArrayJoinTest() +{ + // test wxJoin: + + { + const wxChar *arr[] = { wxT("first"), wxT(""), wxT("second"), wxT("third") }; + wxString expected = wxT("first,,second,third"); + + wxArrayString arrstr(WXSIZEOF(arr), arr); + wxString result = wxJoin(arrstr, wxT(',')); + CPPUNIT_ASSERT( expected == result ); + } + + { + const wxChar *arr[] = { wxT("first, word"), wxT(",,second"), wxT("third,,") }; + wxString expected = wxT("first\\, word,\\,\\,second,third\\,\\,"); + wxString expected2 = wxT("first, word,,,second,third,,"); + + // escaping on: + wxArrayString arrstr(WXSIZEOF(arr), arr); + wxString result = wxJoin(arrstr, wxT(','), wxT('\\')); + CPPUNIT_ASSERT( expected == result ); + + // escaping turned off: + wxString result2 = wxJoin(arrstr, wxT(','), wxT('\0')); + CPPUNIT_ASSERT( expected2 == result2 ); + } + + { + // test is escape characters placed in the original array are just ignored as they should: + const wxChar *arr[] = { wxT("first\\, wo\\rd"), wxT("seco\\nd"), wxT("\\third\\") }; + wxString expected = wxT("first\\\\, wo\\rd,seco\\nd,\\third\\"); + wxString expected2 = wxT("first\\, wo\\rd,seco\\nd,\\third\\"); + + // escaping on: + wxArrayString arrstr(WXSIZEOF(arr), arr); + wxString result = wxJoin(arrstr, wxT(','), wxT('\\')); + CPPUNIT_ASSERT( expected == result ); + + // escaping turned off: + wxString result2 = wxJoin(arrstr, wxT(','), wxT('\0')); + CPPUNIT_ASSERT( expected2 == result2 ); + } +} + +void ArraysTestCase::wxStringArraySplitJoinTest() +{ + wxChar separators[] = { wxT('a'), wxT(','), wxT('_'), wxT(' '), wxT('\\'), + wxT('&'), wxT('{'), wxT('A'), wxT('<'), wxT('>'), + wxT('\''), wxT('\n'), wxT('!'), wxT('-') }; + + // test with a string: split it and then rejoin it: + + wxString str = wxT("This is a long, long test; if wxSplit and wxJoin do work ") + wxT("correctly, then splitting and joining this 'text' _multiple_ ") + wxT("times shouldn't cause any loss of content.\n") + wxT("This is some latex code: ") + wxT("\\func{wxString}{wxJoin}{") + wxT("\\param{const wxArray String\\&}{ arr}, ") + wxT("\\param{const wxChar}{ sep}, ") + wxT("\\param{const wxChar}{ escape = '\\'}}.\n") + wxT("This is some HTML code: ") + wxT("") + wxT("Initial page of Mozilla Firefox") + wxT(""); + + for (size_t i=0; i < WXSIZEOF(separators); i++) + { + wxArrayString arr = wxSplit(str, separators[i]); + CPPUNIT_ASSERT( str == wxJoin(arr, separators[i]) ); + } + + + // test with an array: join it and then resplit it: + + const wxChar *arr[] = + { + wxT("first, second!"), wxT("this is the third!!"), + wxT("\nThat's the fourth token\n\n"), wxT(" - fifth\ndummy\ntoken - "), + wxT("_sixth__token__with_underscores"), wxT("The! Last! One!") + }; + wxArrayString theArr(WXSIZEOF(arr), arr); + + for (size_t i=0; i < WXSIZEOF(separators); i++) + { + wxString string = wxJoin(theArr, separators[i]); + CPPUNIT_ASSERT( theArr == wxSplit(string, separators[i]) ); + } + + wxArrayString emptyArray; + wxString string = wxJoin(emptyArray, _T(';')); + CPPUNIT_ASSERT( string.empty() ); + + CPPUNIT_ASSERT( wxSplit(string, _T(';')).empty() ); + + CPPUNIT_ASSERT_EQUAL( (size_t)2, wxSplit(_T(";"), _T(';')).size() ); +} + void ArraysTestCase::wxObjArrayTest() { {