如何分割字符串保留整个单词?

时间:2010-12-09 12:37:53

标签: c# .net console formatting string-concatenation

我需要将长句分成保留整个单词的部分。每个部分应该给出最大数量的字符(包括空格,点等)。 例如:

int partLenght = 35;
string sentence = "Silver badges are awarded for longer term goals. Silver badges are uncommon."

输出:

1 part: "Silver badges are awarded for"
2 part: "longer term goals. Silver badges are"
3 part: "uncommon."

10 个答案:

答案 0 :(得分:17)

试试这个:

    static void Main(string[] args)
    {
        int partLength = 35;
        string sentence = "Silver badges are awarded for longer term goals. Silver badges are uncommon.";
        string[] words = sentence.Split(' ');
        var parts = new Dictionary<int, string>();
        string part = string.Empty;
        int partCounter = 0;
        foreach (var word in words)
        {
            if (part.Length + word.Length < partLength)
            {
                part += string.IsNullOrEmpty(part) ? word : " " + word;
            }
            else
            {
                parts.Add(partCounter, part);
                part = word;
                partCounter++;
            }
        }
        parts.Add(partCounter, part);
        foreach (var item in parts)
        {
            Console.WriteLine("Part {0} (length = {2}): {1}", item.Key, item.Value, item.Value.Length);
        }
        Console.ReadLine();
    }

答案 1 :(得分:13)

我知道必须有一个很好的LINQ-y方式,所以这里是为了它的乐趣:

var input = "The quick brown fox jumps over the lazy dog.";
var charCount = 0;
var maxLineLength = 11;

var lines = input.Split(' ', StringSplitOptions.RemoveEmptyEntries)
    .GroupBy(w => (charCount += w.Length + 1) / maxLineLength)
    .Select(g => string.Join(" ", g));

// That's all :)

foreach (var line in lines) {
    Console.WriteLine(line);
}

显然,只要查询不是并行的,这段代码就可以工作,因为它依赖于charCount按字顺序递增。

答案 2 :(得分:10)

我一直在测试Jon和Lessan的答案,但是如果你的最大长度需要是绝对的而不是近似的,它们就不能正常工作。当它们的计数器递增时,它不计算一行末尾留下的空白区域。

根据OP的例子运行他们的代码,你得到:

1 part: "Silver badges are awarded for " - 29 Characters
2 part: "longer term goals. Silver badges are" - 36 Characters
3 part: "uncommon. " - 13 Characters

第二行的“是”,应该在第三行。发生这种情况是因为计数器不包括第一行末尾的6个字符。

我想出了以下对Lessan答案的修改:

public static class ExtensionMethods
{
    public static string[] Wrap(this string text, int max)
    {
        var charCount = 0;
        var lines = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        return lines.GroupBy(w => (charCount += (((charCount % max) + w.Length + 1 >= max) 
                        ? max - (charCount % max) : 0) + w.Length + 1) / max)
                    .Select(g => string.Join(" ", g.ToArray()))
                    .ToArray();
    }
}

答案 3 :(得分:5)

使用(空格)拆分字符串,从结果数组中构建新字符串,在每个新段的限制之前停止。

未经测试的伪代码:

string[] words = sentence.Split(new char[] {' '});
IList<string> sentenceParts = new List<string>();
sentenceParts.Add(string.Empty);

int partCounter = 0;    

foreach (var word in words)
{
  if(sentenceParts[partCounter].Length + word.Length > myLimit)
  {
     partCounter++;
     sentenceParts.Add(string.Empty);
  }

  sentenceParts[partCounter] += word + " ";
}

答案 4 :(得分:2)

起初我觉得这可能是一种正则表达式的东西,但这是我对它的看法:

List<string> parts = new List<string>();
int partLength = 35;
string sentence = "Silver badges are awarded for longer term goals. Silver badges are uncommon.";

string[] pieces = sentence.Split(' ');
StringBuilder tempString = new StringBuilder("");

foreach(var piece in pieces)
{
    if(piece.Length + tempString.Length + 1 > partLength) 
    {
        parts.Add(tempString.ToString());
        tempString.Clear();        
    }
    tempString.Append(" " + piece); 
}

答案 5 :(得分:1)

扩展jon的答案;我需要将gg.toArray()切换,并将max更改为(max + 2)以获得最大字符的精确包装。

public static class ExtensionMethods
{
    public static string[] Wrap(this string text, int max)
    {
        var charCount = 0;
        var lines = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        return lines.GroupBy(w => (charCount += w.Length + 1) / (max + 2))
                    .Select(g => string.Join(" ", g.ToArray()))
                    .ToArray();
    }
}

以下是NUnit测试的示例用法:

[Test]
public void TestWrap()
{
    Assert.AreEqual(2, "A B C".Wrap(4).Length);
    Assert.AreEqual(1, "A B C".Wrap(5).Length);

    Assert.AreEqual(2, "AA BB CC".Wrap(7).Length);
    Assert.AreEqual(1, "AA BB CC".Wrap(8).Length);

    Assert.AreEqual(2, "TEST TEST TEST TEST".Wrap(10).Length);
    Assert.AreEqual(2, "  TEST TEST TEST TEST  ".Wrap(10).Length);
    Assert.AreEqual("TEST TEST", "  TEST TEST TEST TEST  ".Wrap(10)[0]);
}

答案 6 :(得分:1)

Joel你的代码中有一个我在这里纠正过的小错误:

public static string[] StringSplitWrap(string sentence, int MaxLength)
{
        List<string> parts = new List<string>();
        string sentence = "Silver badges are awarded for longer term goals. Silver badges are uncommon.";

        string[] pieces = sentence.Split(' ');
        StringBuilder tempString = new StringBuilder("");

        foreach (var piece in pieces)
        {
            if (piece.Length + tempString.Length + 1 > MaxLength)
            {
                parts.Add(tempString.ToString());
                tempString.Clear();
            }
            tempString.Append((tempString.Length == 0 ? "" : " ") + piece);
        }

        if (tempString.Length>0)
            parts.Add(tempString.ToString());

        return parts.ToArray();
}

答案 7 :(得分:0)

这有效:

int partLength = 35;
string sentence = "Silver badges are awarded for longer term goals. Silver badges are uncommon.";
List<string> lines =
    sentence
        .Split(' ')
        .Aggregate(new [] { "" }.ToList(), (a, x) =>
        {
            var last = a[a.Count - 1];
            if ((last + " " + x).Length > partLength)
            {
                a.Add(x);
            }
            else
            {
                a[a.Count - 1] = (last + " " + x).Trim();
            }
            return a;
        });

它给了我:

Silver badges are awarded for 
longer term goals. Silver badges 
are uncommon. 

答案 8 :(得分:0)

虽然CsConsoleFormat†主要用于格式化控制台文本,但它也支持生成纯文本。

var doc = new Document().AddChildren(
  new Div("Silver badges are awarded for longer term goals. Silver badges are uncommon.") {
    TextWrap = TextWrapping.WordWrap
  }
);
var bounds = new Rect(0, 0, 35, Size.Infinity);
string text = ConsoleRenderer.RenderDocumentToText(doc, new TextRenderTarget(), bounds);

并且,如果您确实需要修剪字符串,如问题所示:

List<string> lines = text.Trim()
  .Split(new[] { Environment.NewLine }, StringSplitOptions.None)
  .Select(s => s.Trim())
  .ToList();

除了空格上的自动换行,你还可以正确处理连字符,零宽度空格,不间断空格等。

†​​CsConsoleFormat由我开发。

答案 9 :(得分:0)

似乎每个人都在使用某种形式的“ Split然后重新构建句子” ...

我想我会以这种方式刺耳,就像我的大脑在逻辑上考虑手动执行此操作一样,这是

  • 按长度分割
  • 向后退至最近的空间并使用该块
  • 删除使用过的块并重新开始

代码最终比我期望的要复杂一些,但是我相信它可以处理大多数(全部?)边缘情况-包括长于maxLength的单词,当单词恰好在maxLength上结束时,等等。

这是我的职能:

private static List<string> SplitWordsByLength(string str, int maxLength)
{
    List<string> chunks = new List<string>();
    while (str.Length > 0)
    {
        if (str.Length <= maxLength)                    //if remaining string is less than length, add to list and break out of loop
        {
            chunks.Add(str);
            break;
        }

        string chunk = str.Substring(0, maxLength);     //Get maxLength chunk from string.

        if (char.IsWhiteSpace(str[maxLength]))          //if next char is a space, we can use the whole chunk and remove the space for the next line
        {
            chunks.Add(chunk);
            str = str.Substring(chunk.Length + 1);      //Remove chunk plus space from original string
        }
        else
        {
            int splitIndex = chunk.LastIndexOf(' ');    //Find last space in chunk.
            if (splitIndex != -1)                       //If space exists in string,
                chunk = chunk.Substring(0, splitIndex); //  remove chars after space.
            str = str.Substring(chunk.Length + (splitIndex == -1 ? 0 : 1));      //Remove chunk plus space (if found) from original string
            chunks.Add(chunk);                          //Add to list
        }
    }
    return chunks;
}

测试用法:

string testString = "Silver badges are awarded for longer term goals. Silver badges are uncommon.";
int length = 35;

List<string> test = SplitWordsByLength(testString, length);

foreach (string chunk in test)
{
    Console.WriteLine(chunk);  
}

Console.ReadLine();