尽管这个问题已经很老了,但到目前为止提供的许多解决方案都比必要的复杂和昂贵,正如 user2257198 指出的那样 - 这完全可以用一个简短的单行正则表达式来解决。
但是我发现他的解决方案存在一些问题,包括:在最大宽度之后而不是之前换行,打破未明确包含在字符类中的字符,以及不考虑现有的换行符导致段落的开头被中线截断。
这导致我编写了自己的解决方案:
// Static Width (Plain Regex)
const wrap = (s) => s.replace(
/(?![^\n]{1,32}$)([^\n]{1,32})\s/g, '$1\n'
);
// Dynamic Width (Build Regex)
const wrap = (s, w) => s.replace(
new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, 'g'), '$1\n'
);
奖励功能
- 处理任何不是换行符的字符(例如代码)。
- 正确处理现有的换行符(例如段落)。
- 防止将空格推到换行符的开头。
- 防止在字符串末尾添加不必要的换行符。
解释
主要概念只是找到不包含 new-lines的连续字符序列[^\n]
,直到所需长度,例如 32 {1,32}
。通过在字符类中使用否定^
,它更加宽容,避免丢失诸如标点符号之类的东西,否则必须明确添加:
str.replace(/([^\n]{1,32})/g, '[$1]\n');
// Matches wrapped in [] to help visualise
"[Lorem ipsum dolor sit amet, cons]
[ectetur adipiscing elit, sed do ]
[eiusmod tempor incididunt ut lab]
[ore et dolore magna aliqua.]
"
到目前为止,这只会将字符串精确地切成 32 个字符。它之所以有效,是因为它自己的换行插入标记了第一个序列之后的每个序列的开始。
为了断词,在贪心量词之后需要一个限定符,{1,32}
以防止它选择以单词中间结尾的序列。断字字符可能会在新行的开头产生空格,因此必须使用\b
空白字符。\s
它还必须放在组外以便被吃掉,以防止将最大宽度增加 1 个字符:
str.replace(/([^\n]{1,32})\s/g, '[$1]\n');
// Matches wrapped in [] to help visualise
"[Lorem ipsum dolor sit amet,]
[consectetur adipiscing elit, sed]
[do eiusmod tempor incididunt ut]
[labore et dolore magna]
aliqua."
现在它在限制之前的单词上中断,但最后一个单词和句点在最后一个序列中不匹配,因为没有终止空格。
可以将“或字符串结尾”选项(\s|$)
添加到空格以扩展匹配,但最好完全阻止匹配最后一行,因为它会导致在结尾。为了实现这一点,可以在之前添加完全相同序列的负前瞻,但使用字符串结尾字符而不是空白字符:
str.replace(/(?![^\n]{1,32}$)([^\n]{1,32})\s/g, '[$1]\n');
// Matches wrapped in [] to help visualise
"[Lorem ipsum dolor sit amet,]
[consectetur adipiscing elit, sed]
[do eiusmod tempor incididunt ut]
labore et dolore magna aliqua."