自动换行是现代文本编辑器中必备功能之一.
你知道如何处理自动换行吗?自动换行的最佳算法是什么?
更新:如果文本是几百万行,我怎么能快速进行自动换行?
更新:为什么我需要解决方案?因为我的项目必须绘制具有各种缩放级别和同时漂亮外观的文本.
已更新:运行环境是Windows Mobile设备.最大600MHz速度,内存尺寸非常小.
更新:我应该如何处理行信息?我们假设原始数据有三行.
THIS IS LINE 1. THIS IS LINE 2. THIS IS LINE 3.
分词后的文字将显示如下:
THIS IS LINE 1. THIS IS LINE 2. THIS IS LINE 3.
我应该再分配3行吗?还是其他任何建议?
这是我用C#编写的自动换行算法.翻译成其他语言应该相当容易(除了可能IndexOfAny
).
static char[] splitChars = new char[] { ' ', '-', '\t' };
private static string WordWrap(string str, int width)
{
string[] words = Explode(str, splitChars);
int curLineLength = 0;
StringBuilder strBuilder = new StringBuilder();
for(int i = 0; i < words.Length; i += 1)
{
string word = words[i];
// If adding the new word to the current line would be too long,
// then put it on a new line (and split it up if it's too long).
if (curLineLength + word.Length > width)
{
// Only move down to a new line if we have text on the current line.
// Avoids situation where wrapped whitespace causes emptylines in text.
if (curLineLength > 0)
{
strBuilder.Append(Environment.NewLine);
curLineLength = 0;
}
// If the current word is too long to fit on a line even on it's own then
// split the word up.
while (word.Length > width)
{
strBuilder.Append(word.Substring(0, width - 1) + "-");
word = word.Substring(width - 1);
strBuilder.Append(Environment.NewLine);
}
// Remove leading whitespace from the word so the new line starts flush to the left.
word = word.TrimStart();
}
strBuilder.Append(word);
curLineLength += word.Length;
}
return strBuilder.ToString();
}
private static string[] Explode(string str, char[] splitChars)
{
List parts = new List();
int startIndex = 0;
while (true)
{
int index = str.IndexOfAny(splitChars, startIndex);
if (index == -1)
{
parts.Add(str.Substring(startIndex));
return parts.ToArray();
}
string word = str.Substring(startIndex, index - startIndex);
char nextChar = str.Substring(index, 1)[0];
// Dashes and the likes should stick to the word occuring before it. Whitespace doesn't have to.
if (char.IsWhiteSpace(nextChar))
{
parts.Add(word);
parts.Add(nextChar.ToString());
}
else
{
parts.Add(word + nextChar);
}
startIndex = index + 1;
}
}
它相当原始 - 它在空格,制表符和短划线上分开.它确保破折号坚持到它之前的单词(这样你就不会与栈\正溢出结束)虽然不利于移动小连字符的话换行,而不是分裂他们.如果它们对于一条线太长,它会分裂单词.
它也具有相当的文化特色,因为我对其他文化的包装规则知之甚少.
Donald E. Knuth在他的TeX排版系统中对断线算法做了大量工作.这可以说是最好的断线算法之一 - 在结果的视觉外观方面"最好".
他的算法避免了贪婪线填充的问题,你可以得到一条非常密集的线,然后是一条非常松散的线.
可以使用动态编程来实现有效的算法.
关于TeX破线的论文.
我不知道是否有人会读到这个看到这个问题有多久,但我最近有机会写一个自动换行功能,我想分享我想出的东西.我使用的TDD方法几乎与Go示例中的方法一样严格.我开始测试包裹字符串"Hello,world!" 在80宽度应返回"你好,世界!" 显然,最简单的方法是不改变输入字符串.从那开始,我做了越来越复杂的测试,最终得到了一个递归解决方案(至少对我而言)非常有效地处理任务.
递归解决方案的伪代码:
Function WordWrap (inputString, width) Trim the input string of leading and trailing spaces. If the trimmed string's length is <= the width, Return the trimmed string. Else, Find the index of the last space in the trimmed string, starting at width If there are no spaces, use the width as the index. Split the trimmed string into two pieces at the index. Trim trailing spaces from the portion before the index, and leading spaces from the portion after the index. Concatenate and return: the trimmed portion before the index, a line break, and the result of calling WordWrap on the trimmed portion after the index (with the same width as the original call).
这只包装在空格中,如果你想包装一个已经包含换行符的字符串,你需要在换行符处拆分它,将每个部分发送到这个函数,然后重新组合字符串.即便如此,在运行在快速机器上的VB.NET中,这可以处理大约20 mb/sec.
我不知道任何具体的算法,但下面不会大致说明它应该如何工作:
对于当前文本大小,字体,显示大小,窗口大小,边距等,确定一行上可以容纳的字符数(如果是固定类型),或者一行上可以容纳多少像素(如果不是固定类型).
逐字逐行,计算自行开始以来已记录的字符数或像素数.
当您查看该行的最大字符/像素时,请移回最后一个空格/标点符号,将所有文本移动到下一行.
重复,直到您浏览文档中的所有文本.
问题:在.net中,自动换行功能内置于TextBox等控件中.我确信其他语言也存在类似的内置功能.您是否有理由不想使用预先构建的解决方案?这似乎是重新发明轮子的方式.