【问题标题】:Elegant way to split string into 2 strings on word boundaries to minimize length difference在单词边界上将字符串拆分为 2 个字符串以最小化长度差异的优雅方法
【发布时间】:2010-09-09 20:33:10
【问题描述】:

我现在有一个可行的解决方案,但对于如此(看似)简单的事情来说,它似乎真的很难看。

我尝试在添加单词超过中间标记时打破它,在添加单词之前和之后都分裂,但根据单词的长度,它可能会在第一行或第二行不平衡。

在复杂修复之前我最初遇到问题的示例输入:

输入"Macaroni Cheese""Cheese Macaroni"

应该分别输出"Macaroni<br/> Cheese""Cheese<br/> Macaroni"

但更简单的解决方案要么适用于第一个,但不能适用于第二个,或者反过来。

这就是我所拥有的,但我想知道是否有更优雅的方式来做到这一点。

public string Get2LineDisplayText(string original)
{
    string[] words = original.Split(new[] {' ', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);

    //Degenerate case with only 1 word
    if (words.Length <= 1)
    {
        return original;
    }

    StringBuilder builder = new StringBuilder();
    builder.Append(words[0]); //Add first word without prepending space
    bool addedBr = false;
    foreach (string word in words.Skip(1))
    {
        if (builder.Length + word.Length < original.Length / 2) //Word fits on the first line without passing halfway mark
        {
            builder.Append(' ' + word);
        }
        else if (!addedBr) //Adding word goes over half, need to see if it's more balanced on the 1st or 2nd line
        {
            int diffOnLine1 = Math.Abs((builder.Length + word.Length) - (original.Length - builder.Length - word.Length));
            int diffOnLine2 = Math.Abs((builder.Length) - (original.Length - builder.Length));
            if (diffOnLine1 < diffOnLine2)
            {
                builder.Append(' ' + word);
                builder.Append("<br/>");
            }
            else
            {
                builder.Append("<br/>");
                builder.Append(' ' + word);
            }
            addedBr = true;
        }
        else //Past halfway and already added linebreak, just append
        {
            builder.Append(' ' + word);
        }
    }

    return builder.ToString();
}

示例输入/输出:

【问题讨论】:

  • 您能否提供一些(简短的)示例输入和预期输出?
  • @Jeff 以前有,但现在更清楚了。
  • +1 给所有人,但接受了最容易理解的那个。

标签: c# string


【解决方案1】:

这是我想出的:

    public static string Get2Lines(string input)
    {
        //Degenerate case with only 1 word
        if (input.IndexOf(' ') == -1)
        {
            return input;
        }
        int mid = input.Length / 2;

        int first_index_after = input.Substring(mid).IndexOf(' ') + mid;
        int first_index_before = input.Substring(0, mid).LastIndexOf(' ');

        if (first_index_after - mid < mid - first_index_before)
            return input.Insert(first_index_after, "<BR />");
        else
            return input.Insert(first_index_before, "<BR />");
    }

【讨论】:

  • 只添加了一个单词的检查。
【解决方案2】:
public static string Get2LineDisplayText(string original)
{
    //Degenerate case with only 1 word
    if (!original.Any(Char.IsWhiteSpace))
    {
        return original;
    }
    int mid = original.Length / 2;
    if (!Char.IsWhiteSpace(original[mid]))
    {
        for (int i = 1; i < mid; i += i)
        {
            if (Char.IsWhiteSpace(original[mid + i]))
            {
                mid = mid + i;
                break;
            }
            if (Char.IsWhiteSpace(original[mid - i]))
            {
                mid = mid - i;
                break;
            }
        }
    }

    return original.Substring(0, mid)
           + "<br />" + original.Substring(mid + 1);
}

【讨论】:

  • 在了解目标是什么之后,我会想出这样的答案。
  • 不编译,但在修复错别字并添加一个单词的大小写检查后,它可以工作。所以本质上它是一个广度优先搜索最接近中间空白字符的索引。聪明。
  • 希望你不介意我用可编译的版本编辑它。
【解决方案3】:

我试了一下,结果:

String splitInMiddle(String s) {
    int middle = s.length() / 2;
    int right = s.indexOf(" ",middle);
    int left = s.lastIndexOf(" ",middle);

    int split = right;
    if ((right < 0) || (left + right > 2*middle)) {
        split = left;
    }
    return s.substring(0, split) + "<br/>\n" + s.substring(split + 1);
}

其原理是查找后面的第一个空格和前面的最后一个空格。 如果左边比右边更近,就选那个。

然后用 CR 而不是空格粘贴我想要的部分。

【讨论】:

    猜你喜欢
    • 2020-03-15
    • 2021-09-07
    • 2014-11-09
    • 1970-01-01
    • 1970-01-01
    • 2012-11-20
    • 1970-01-01
    相关资源
    最近更新 更多