简体   繁体   中英

C# - Sorting Strings

I have a RichTextBox that looks similar to this:

TEXT  TEXT-1     227.905  174.994  180  1111
TEXT  DIFTEXT    227.905  203.244  180  9999
TEXT  DIFTEXT2   242.210  181.294  180  2222
TEXT  TEXT-2     236.135  198.644  90   ABC1111
TEXT  SOMETEXT   250.610  201.594  0    DDDD
TEXT  OTHERTEXT  269.665  179.894  180  4444
TEXT  OTHERTEXT  269.665  198.144  180  1111

And I would like to sort it in a special order. Let's say I want to sort it by the last value (column 6) in this sequence:

1111, 2222, 4444, 9999, DDDD

AND then sort it secondly by the 2nd column alphabetically.

So the updated file would look like this NOTE: Since there is no sorting for "ABC1111" it matches the 1111 instead :

TEXT  OTHERTEXT  269.665  179.894  180  1111
TEXT  TEXT-1     227.905  174.994  180  1111
TEXT  TEXT-2     236.135  198.644  90   ABC1111   #See note above
TEXT  DIFTEXT2   242.210  181.294  180  2222
TEXT  OTHERTEXT  269.665  198.144  180  4444
TEXT  DIFTEXT    227.905  203.244  180  9999
TEXT  SOMETEXT   250.610  201.594  0    DDDD

And once it is in this format I would like to write it back to the RichTextBox .

  • How can I do this?

The TextBox class will already give you an array of lines without any work on your part. Array.Sort will allow you to pass in your own code that compares the lines. I can't think of an elegant way to implement awkward sorting rules but the way I'd solve your problem would look something like the code below. Note that you do need to pull out the lines array into a temp variable so you can set it back, otherwise the setter isn't called and the textbox isn't updated.

private static void SortRichTextBox(RichTextBox richTextBox)
{
    var lineArray = richTextBox.Lines;
    Array.Sort(lineArray, delegate(string a, string b)
    {
        // I omitted Important corner cases like malformed lines,
        // empty strings, and nulls.

        // Additional sort cases can be added to the array as needed.
        var sortOrder = new[] { "1111", "2222", "4444", "9999", "DDDD" };
        var aTokens = a.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        var bTokens = b.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

        var aSignifier = Array.FindIndex(sortOrder, item => aTokens[5].Contains(item));
        var bSignifier = Array.FindIndex(sortOrder, item => bTokens[5].Contains(item));

        // This is where your first rule is being implemented, based on the data
        // from the last column.  The math is unimportant, what matters is the 
        // return being positive vs. negative vs. zero.
        if (aSignifier != bSignifier)
            return aSignifier - bSignifier;

        // This is where your second rule is being implemented, as a backup when
        // the result from the first rule indicates equality.  We're just falling
        // through to ordinary string comparison on the second column.
        return aTokens[1].CompareTo(bTokens[1]);
    });

    // We want to call the setter to get the textbox to update.
    richTextBox.Lines = lineArray;
}

I tested this, and it looks like it works, at least with your one sample data set.

static void Main(string[] args)
{
    string input =
        "TEXT  TEXT-1     227.905  174.994  180  1111\n" +
        "TEXT  DIFTEXT    227.905  203.244  180  9999\n" +
        "TEXT  DIFTEXT2   242.210  181.294  180  2222\n" +
        "TEXT  TEXT-2     236.135  198.644  90   ABC1111\n" +
        "TEXT  SOMETEXT   250.610  201.594  0    DDDD\n" +
        "TEXT  OTHERTEXT  269.665  179.894  180  4444\n" +
        "TEXT  OTHERTEXT  269.665  198.144  180  1111";

    string[] lines = input.Split('\n');
    string[][] grid = new string[lines.Length][];
    for (int i = 0; i < lines.Length; i++)
    {
        grid[i] = lines[i].Split((char[])null, StringSplitOptions.RemoveEmptyEntries); // split on whitespace
    }

    IEnumerable<string[]> results = grid
        .OrderBy(gridRow => TransformColumn6(gridRow[5]))
        .ThenBy(gridRow => gridRow[1]);

    foreach (string[] gridRow in results)
    {
        Debug.WriteLine(string.Join(" ", gridRow));
    }
}

private static string TransformColumn6(string input)
{
    if (char.IsDigit(input[0]))
    {
        // string is a bunch of numbers, use as-is.
        return input;
    }

    int digitIndex = input.IndexOfAny(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
    if (digitIndex == -1)
    {
        // string has no numbers, use as-is, will be sorted in alphabetical order after all the numbers.
        return input;
    }

    // string has a number, remove the stuff at the beginning.
    return input.Substring(digitIndex);
}

Results (column formatting added manually):

TEXT OTHERTEXT 269.665 198.144 180 1111 
TEXT TEXT-1    227.905 174.994 180 1111 
TEXT TEXT-2    236.135 198.644 90  ABC1111 
TEXT DIFTEXT2  242.210 181.294 180 2222 
TEXT OTHERTEXT 269.665 179.894 180 4444 
TEXT DIFTEXT   227.905 203.244 180 9999 
TEXT SOMETEXT  250.610 201.594 0   DDDD 

If the rules for sorting the last column got much more complicated, then you'd probably want to implement a custom Comparator, instead of using the default string comparison.

I would copy the data to a DataTable and use it to sort by multiple columns and then copy the data back.

http://msdn.microsoft.com/en-us/library/system.data.dataview.sort.aspx

Bearing in mind the ambiguities highlighted in my comment. You could parse the string into an array of objects ( String.Split or String.Substring may help you here, depending on the structure of your data).

Then use Array.Sort (first by the property representing column 2, then by column 6) to sort your array, as per the example here which sorts an array of a custom type.

Create a .ToString method for your class to convert each object back into the correct format.

Going off your previous question, if you convert them to objects you can use linq to order them.

http://msdn.microsoft.com/en-us/library/bb383982.aspx

Assuming you can get them into objects, you can use from in order by , select

To get around the abc1111 issue, I would create a separate property that you're sorting on that only contains the 1111.

Also, please remember to accept answers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM