Interesting, your implementation is a bit more sophisticated, but at least it proves that this kind of comparator is a common concern.

Would the Collections folks reconsider their decision and accept such a comparator due to "popular" demand ? :) I'm attaching my implementation.

Emmanuel Bourg


Henri Yandell wrote:

I offered one of those to Collections a while back but it was turned
down as it chose one specific way to do the ordering:

http://www.osjava.org/genjava/multiproject/gj-core/xref/com/generationjava/compare/NumericStringComparator.html

For your submission, I think as a comparator of Strings to Collections
makes the most sense as it deals with java.util.Comparator and Lang
doesn't.

Anything in my class is up for grabs if you want any of it :)

Hen

On Wed, 05 Jan 2005 01:13:13 +0100, Emmanuel Bourg <[EMAIL PROTECTED]> wrote:

Hi all, I have developped a File comparator to sort properly the files
with a number in the name. For example, instead of sorting with the
lexicographic order :

foo1.txt < foo10.txt < foo2.txt

it compares the numbers when the files share the same prefix and returns
this order :

foo1.txt < foo2.txt < foo10.txt

This is how the "Sort by name" works in the Windows XP file explorer.
Since it's quite low level I think it could be included in a commons
project, but I'm not sure where:

- in [io] as a getComparator() method in FileUtils or as a
FilenameComparator class ?

- in [lang] as a String comparator ?

- nowhere because this was already implemented in another well known
project and I wasted my time reinventing the wheel ;)

Emmanuel Bourg
import java.io.*;
import java.util.*;

/**
 * An improved lexicographic comparator handling a number in a string as a 
single
 * character. Unlike the lexicographic order where "foo1.txt" &lt; "foo10.txt" 
&lt; "foo2.txt"
 * here we have "foo1.txt" &lt; "foo2.txt" &lt; "foo10.txt".
 *
 * @author Emmanuel Bourg
 * @version $Revision$, $Date$
 */
public class NumericStringComparator implements Comparator<String>
{
    public int compare(String name1, String name2)
    {
        int index1 = 0;
        int index2 = 0;

        while (true)
        {
            String token1 = getToken(name1, index1);
            String token2 = getToken(name2, index2);

            if (token1 == null && token2 == null)
            {
                // no more tokens for each name, they are equal
                return 0;
            }

            if (token1 == null)
            {
                // the first name is shorter, it goes first
                return -1;
            }

            if (token2 == null)
            {
                // the second name is shorter, it goes first
                return 1;
            }

            int comp = compareToken(token1, token2);
            if (comp == 0)
            {
                // the tokens are equal, move to the next tokens
                index1 = index1 + token1.length();
                index2 = index2 + token2.length();
            }
            else
            {
                return comp;
            }
        }
    }

    /**
     * Extract from the string  the next token starting at the specified index.
     *
     * @param string the string to parse
     * @param index  the beginning of the token
     */
    String getToken(String string, int index)
    {
        if (string == null || string.length() == 0 || index == string.length())
        {
            return null;
        }
        else
        {
            // are we parsing a string or a number ?
            boolean type = Character.isDigit(string.charAt(index));

            // move forward until a different character type is detected
            int end = index + 1;
            while (end < string.length() && 
Character.isDigit(string.charAt(end)) == type)
            {
                end++;
            }

            return string.substring(index, end);
        }
    }

    /**
     * Tells if the specified string is a number.
     */
    boolean isNumber(String string)
    {
        if (string == null || string.length() == 0)
        {
            return false;
        }
        else
        {
            return Character.isDigit(string.charAt(0));
        }
    }

    /**
     * Compare two tokens according to their types (string or number).
     */
    int compareToken(String token1, String token2)
    {
        if (isNumber(token1) && isNumber(token2))
        {
            return Integer.parseInt(token1) - Integer.parseInt(token2);
        }
        else
        {
            return token1.compareTo(token2);
        }
    }
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to