Перенос слова по правилам русского языка
Мне нужно перенести предложение на новую строку по правилам русского языка. В одной строчке не должно быть больше "n" символов, если лимит привышается то перенести слово на следующую строку.
Моя попытка кода:
"После использования этой команды вы получите информацию и статистику о себе".replace(/(.{42})/g,"$1\-\n")
Регс переносит слово если строка превышает лимит в 42 символа. Однако перенос осуществляется не по правилам.
Ответы (2 шт):
Если под правилами понимать то что нельзя переносить/оставлять одну букву, то попробуйте эту функцию:
function createLineBreaks(text) {
return text.split(/(?=(?<!.{42,})(?:(?<=[^ ])[^ ](?=[^ ]{2})| )(?!.*(?<!.{42,})(?:(?<=[^ ])[^ ](?=[^ ]{2})| )))/).join("\n");
}
UPD:
Да, немножко накосячил (простите :) ). Не знал как split в js'се работает... Вот обновлённая версия:
function createLineBreaks(text) {
text = new Array(text);
let lastIndex = 0;
while (text[lastIndex].length > 42) {
const temp = text[lastIndex].split(/(?=(?<!.{42,})(?:(?<=[a-zA-Zа-яА-Я]{2})[a-zA-Zа-яА-Я](?=[a-zA-Zа-яА-Я])| )(?!.*(?<!.{42,})(?:(?<=[a-zA-Zа-яА-Я]{2})[a-zA-Zа-яА-Я](?=[a-zA-Zа-яА-Я])| ))) ?/);
text[lastIndex] = temp[0];
text[lastIndex + 1] = temp[1];
lastIndex++;
}
return text.join("\n");
}
функция подлиннее, но эти баги ушли.
Непосредственно регуляркой эту задачу не решить. Как отметили выше, чтобы строго следовать правилам, нужно учитывать морфемное строение слова.
Но, взяв в подмогу регулярное выражение, всё же можно добиться неплохого результата.
Для начала несколько замечаний:
- Правила переноса не являются строгими, а носят скорее рекомендательный характер.
- Базовое правило переноса - по слогам.
- Правила разделения на слоги отличаются в школьной программе и в вузах. Совокупно, правила переноса ближе к разделению на слоги по школьной программе.
- Чаще переносится часть слова с согласной в начале.
- Возможен перенос слова целиком (без разделения), тогда тире в конце строки не требуется.
- Одну букву не переносим.
Учитывая выше сказанное, я составил регулярное выражение:
[бвгджзйклмнпрстфхцчшщ]+[аеёиоуыэюя][бвгджзйклмнпрстфхцчшщ](?=[бвгджзйклмнпрстфхцчшщьъ ])[ьъй]?|[бвгджзйклмнпрстфхцчшщ]+[аеёиоуыэюя][й]?|[аеёиоуыэюя][бвгджзйклмнпрстфхцчшщ](?=[бвгджзйклмнпрстфхцчшщьъ ])[ъь]?|[аеёиоуыэюя](?=[а-я]{2})|(?<= +)[^\s]+(?= +|$)
Не обращайте внимания на невыделенные окончания у некоторых слов. Меня интересуют индексы слогов, т.е. где они начинаются. Также добавляются союзы и всё, что не соответствует основным правилам РВ.
Далее, алгоритм простой, просматриваем индексы, если вышли за лимит, делаем перенос на предыдущем индексе. Однобуквенные слоги в начале слова игнорируем:
const regex = /[бвгджзйклмнпрстфхцчшщ]+[аеёиоуыэюя][бвгджзйклмнпрстфхцчшщ](?=[бвгджзйклмнпрстфхцчшщьъ ])[ьъй]?|[бвгджзйклмнпрстфхцчшщ]+[аеёиоуыэюя][й]?|[аеёиоуыэюя][бвгджзйклмнпрстфхцчшщ](?=[бвгджзйклмнпрстфхцчшщьъ ])[ъь]?|[аеёиоуыэюя](?=[а-я]{2})|(?<= +)[^\s]+(?= +|$)/gmi;
function wordWrap(text, limit) {
let out = '', offset = 0, prev = 0, pass = false;
let matches = text.matchAll(regex);
for (const m of matches) {
if (m.index - offset > limit) {
let hyphen = text[prev - 1] == ' ' ? '' : '-';
out += text.substring(offset, prev) + hyphen + '\n';
offset = prev;
}
if (!pass) prev = m.index;
pass = m[0].length < 2;
}
out += text.substring(offset);
return out;
}
input.oninput = text.oninput = () => dtext.innerText = wordWrap(text.value, input.value);
input.oninput();
<input id="text" type="text" style="width: 100%" value="Правила переноса слов рекомендуют переносить слова с одной строки на другую по слогам с учетом морфемного строения. В письменной речи слова переносятся с одной строки на другую в соответствии с определенными правилами переноса.">
<hr>
<input id="input" type="number" min="3" max="100" value="42">
<pre id="dtext" style=""></pre>
На мой взгляд, вполне удовлетворительный результат. Можно еще похимичить с регуляркой, например, добавить приставки.