2
\$\begingroup\$

The task: sort the characters in a string, as provided by the ICU UnicodeString. This is because I want to be able to find anagrams using the suggestion from "Programming Pearls", which is, find "signatures" for each word in the dictionary, then sort according to these. It is enough if it works for the European languages and scripts.

The input file is for example the word list found in /usr/share/dict/words. For now, I just read from standard input, print them to standard output, sort the characters in the word, and print the sorted word:

$ cat sort-each-word.cpp 
#include <iostream>
#include <algorithm>
#include "unicode/ustream.h"
#include "unicode/unistr.h"
#include "unicode/schriter.h"

int main()
{
    icu::UnicodeString word;
    while (std::cin >> word) {
        std::cout << word << '\t';
        auto n = word.length();
        UChar *begin = word.getBuffer(n+1);
        UChar *end = begin + n;
        std::sort(begin, end);
        *(begin+n) = 0;
        word.releaseBuffer(n);
        std::cout << word << '\n';
    }
}

To compile it:

g++  -pedantic -Wall -O2 --std=c++0x    -L/usr/lib -licui18n -licuuc -licudata   -licuio   sort-each-word.cpp   -o sort-each-word

Then, for example:

$ cat naivete
naiveté
$ < naivete ./sort-each-word
naiveté aeintvé

I feel most uncomfortable about getBuffer(n+1), where n is the current string length. I need the extra space to terminate the sorted string, but must I check that length() + 1 =< getCapacity()?

Other suggestions and comments are of course most welcome.

\$\endgroup\$
2
\$\begingroup\$

You should not need the n + 1, since the UnicodeString keeps track of the string's length, no matter whether it is null-terminated or not. It's in the documentation, you just need to read it.

So the line *(begin+n) = 0; is unnecessary. Especially since you tell icu that the new length is n, which means that begin[n] will be ignored anyway.

A general remark: maybe the code is even easier to read like follows:

auto len = word.length();
UChar *buf = word.getBuffer(len);
std::sort(buf, buf + len);
word.releaseBuffer(len);

I renamed n to len, renamed begin to buf and removed end altogether. Given the idiomatic C++ usage of the (begin, begin + len) pattern, it saves some code and makes the intention of the variables a little clearer.

If you want to support full unicode, you should not use UChar, since that is only a UTF-16 code unit and therefore not able to handle emojis, extended CJK ideographs, byzantine musical symbols and several more scripts starting at U+10000 and beyond.

\$\endgroup\$
  • \$\begingroup\$ Great suggestions. I will also edit the question to explain why I need this code (no need to support emojis, for example). This is the first time I am attempting to use ICU and there is still a lot of reading ahead of me until I know what I'm doing. \$\endgroup\$ – XXX Jul 31 '16 at 19:46
  • \$\begingroup\$ By the way, regarding your primary question: yes, it is ok to call .getBuffer(length() * 2) or even beyond that. This call is meant for low-level changes to the string. It basically says: get me a buffer of the size I want and fill it with the current string's value, so I can inspect and manipulate it. The size can be arbitrary. You should just check its return value for not being 0. \$\endgroup\$ – Roland Illig Jul 31 '16 at 19:51
  • \$\begingroup\$ You say, "it is ok to call .getBuffer(length() * 2) or even beyond that"; it that documented somewhere, or is it in the source? So far I have mainly been reading the documentation, for example, here. It leaves me with the impression that I get a pointer to an already existing buffer... Is it copied if minCapacity > getCapacity()? \$\endgroup\$ – XXX Jul 31 '16 at 20:00
  • \$\begingroup\$ The documentation is a bit unclear at this point. It should have said that the internal buffer gets resized to fit the minCapacity. It does guarantee though that the internal buffer, after returning, is large enough, which can only mean that it gets resized during the call to getBuffer. Another hint to this is that an out of memory condition is possible during this call. This wouldn't be the case if the function just returned the existing internal buffer. \$\endgroup\$ – Roland Illig Jul 31 '16 at 20:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.