我希望将一个字符串分成指定最大长度的行,如果可能的话,不分割任何单词(如果有一个超过最大行长度的单词,则必须将其拆分)。
与往常一样,我敏锐地意识到字符串是不可变的,并且最好使用StringBuilder类。我已经看过将字符串拆分为单词的示例,然后使用StringBuilder类构建行,但下面的代码对我来说似乎“更整洁”。
我在描述中提到“最好”而不是“最有效”,因为我也对代码的“口才”感兴趣。字符串永远不会很大,通常会分成2行或3行,并且不会发生数千行。
下面的代码真的很糟糕吗?
private static IEnumerable<string> SplitToLines(string stringToSplit, int maximumLineLength)
{
stringToSplit = stringToSplit.Trim();
var lines = new List<string>();
while (stringToSplit.Length > 0)
{
if (stringToSplit.Length <= maximumLineLength)
{
lines.Add(stringToSplit);
break;
}
var indexOfLastSpaceInLine = stringToSplit.Substring(0, maximumLineLength).LastIndexOf(' ');
lines.Add(stringToSplit.Substring(0, indexOfLastSpaceInLine >= 0 ? indexOfLastSpaceInLine : maximumLineLength).Trim());
stringToSplit = stringToSplit.Substring(indexOfLastSpaceInLine >= 0 ? indexOfLastSpaceInLine + 1 : maximumLineLength);
}
return lines.ToArray();
}
答案 0 :(得分:8)
即使这篇文章是3年,我也希望使用Regex
提供更好的解决方案来实现同样的目标:
如果您希望拆分字符串,然后使用要显示的文本,您可以使用:
public string SplitToLines(string stringToSplit, int maximumLineLength)
{
return Regex.Replace(stringToSplit, @"(.{1," + maximumLineLength +@"})(?:\s|$)", "$1\n");
}
如果另一方面你需要一个集合,你可以使用它:
public MatchCollection SplitToLines(string stringToSplit, int maximumLineLength)
{
return Regex.Matches(stringToSplit, @"(.{1," + maximumLineLength +@"})(?:\s|$)");
}
MatchCollection
的工作方式几乎与Array
答案 1 :(得分:6)
这是一个解决方案:
IEnumerable<string> SplitToLines(string stringToSplit, int maximumLineLength)
{
var words = stringToSplit.Split(' ').Concat(new [] { "" });
return
words
.Skip(1)
.Aggregate(
words.Take(1).ToList(),
(a, w) =>
{
var last = a.Last();
while (last.Length > maximumLineLength)
{
a[a.Count() - 1] = last.Substring(0, maximumLineLength);
last = last.Substring(maximumLineLength);
a.Add(last);
}
var test = last + " " + w;
if (test.Length > maximumLineLength)
{
a.Add(w);
}
else
{
a[a.Count() - 1] = test;
}
return a;
});
}
答案 2 :(得分:6)
我认为你的解决方案太糟糕了。但是,我确实认为你应该将你的三元分解成if if因为你正在测试两次相同的条件。您的代码也可能有错误。根据您的描述,您似乎需要行&lt; = maxLineLength,但您的代码会计算最后一个单词后的空格,并在&lt; =比较中使用它,从而有效地&lt;修剪过的字符串的行为。
这是我的解决方案。
private static IEnumerable<string> SplitToLines(string stringToSplit, int maxLineLength)
{
string[] words = stringToSplit.Split(' ');
StringBuilder line = new StringBuilder();
foreach (string word in words)
{
if (word.Length + line.Length <= maxLineLength)
{
line.Append(word + " ");
}
else
{
if (line.Length > 0)
{
yield return line.ToString().Trim();
line.Clear();
}
string overflow = word;
while (overflow.Length > maxLineLength)
{
yield return overflow.Substring(0, maxLineLength);
overflow = overflow.Substring(maxLineLength);
}
line.Append(overflow + " ");
}
}
yield return line.ToString().Trim();
}
它比您的解决方案稍长,但它应该更直接。它还使用StringBuilder,因此对于大字符串来说它要快得多。我对20,000个单词进行了基准测试,范围从1到11个字符,每个字符分成10个字符宽度的行。我的方法在14ms内完成,而你的方法则为1373ms。
答案 3 :(得分:2)
试试这个(未经测试的)
private static IEnumerable<string> SplitToLines(string value, int maximumLineLength)
{
var words = value.Split(' ');
var line = new StringBuilder();
foreach (var word in words)
{
if ((line.Length + word.Length) >= maximumLineLength)
{
yield return line.ToString();
line = new StringBuilder();
}
line.AppendFormat("{0}{1}", (line.Length>0) ? " " : "", word);
}
yield return line.ToString();
}
答案 4 :(得分:0)
我的要求是在30个字符的限制之前的最后一个空格处换行。 所以这是我的做法。希望这对任何人都有帮助。
private string LineBreakLongString(string input)
{
var outputString = string.Empty;
var found = false;
int pos = 0;
int prev = 0;
while (!found)
{
var p = input.IndexOf(' ', pos);
{
if (pos <= 30)
{
pos++;
if (p < 30) { prev = p; }
}
else
{
found = true;
}
}
outputString = input.Substring(0, prev) + System.Environment.NewLine + input.Substring(prev, input.Length - prev).Trim();
}
return outputString;
}
答案 5 :(得分:0)
使用递归方法和ReadOnlySpan<T>
(已测试)的方法
public static void SplitToLines(ReadOnlySpan<char> stringToSplit, int index, ref List<string> values)
{
if (stringToSplit.IsEmpty || index < 1) return;
var nextIndex = stringToSplit.IndexOf(' ');
var slice = stringToSplit.Slice(0, nextIndex < 0 ? stringToSplit.Length : nextIndex);
if (slice.Length <= index)
{
values.Add(slice.ToString());
nextIndex++;
}
else
{
values.Add(slice.Slice(0, index).ToString());
nextIndex = index;
}
if (stringToSplit.Length <= index) return;
SplitToLines(stringToSplit.Slice(nextIndex), index, ref values);
}