Stack Overflow на русском Asked by Kolhoznik on February 26, 2021
Есть определённое количество слов, около 2000 тыс. Можно ли их как-то программно разбить по слогам?
Неплохим выбором, на мой взгляд, будет использовать словарь. Стандартная реализация.
Естественно, чтобы разбить слова на слоги, сначала эти слова нужно добавить в словарь, но зато впоследствии можно будет в любой момент к нему обратиться.
Main
import Tree.RootNode;
import Exceptions.NoSuchWordException;
import java.io.*;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
try {
RootNode rootNode;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dictionary.dat"))) {
rootNode = (RootNode) ois.readObject();
System.out.println("The file was read successfully!");
} catch (ClassNotFoundException | IOException ioe) {
System.out.println("File read failed. Creating new dictionary.");
rootNode = new RootNode();
}
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dictionary.dat"));
String command;
Scanner scanner = new Scanner(System.in);
while (!(command = scanner.nextLine()).trim().equals("exit")) {
String[] strings = command.split("\s+");
if (command.equals("save")) {
try {
oos.writeObject(rootNode);
System.out.println("The dictionary was saved successfully!");
} catch (IOException e) {
System.out.println("Save failed.");
}
continue;
} else if (strings.length < 2) {
System.out.println("The command must have an argument!");
continue;
}
switch (strings[0]) {
case "add":
case "put":
case "push":
case "shove":
rootNode.addWord(strings[1], Arrays.copyOfRange(strings, 2, strings.length));
System.out.println("Word '" + strings[1] + "' added successfully!");
break;
case "extract":
case "get":
try {
rootNode.extractWord(strings[1]).printSyllables();
} catch (NoSuchWordException nswee) {
System.out.println("There is no such word!");
}
break;
case "delete":
case "remove":
case "erase":
try {
rootNode.deleteWord(strings[1]);
System.out.println("Word '" + strings[1] + "' deleted successfully!");
} catch (NoSuchWordException nswee) {
System.out.println("There is no such word!");
}
break;
default:
System.out.println("There is no such command!");
break;
}
}
oos.writeObject(rootNode);
System.out.println("Dictionary is saved. Exiting...");
} catch (IOException e) {
e.printStackTrace();
}
}
}
RootNode
package Tree;
public class RootNode extends Node {
public Node extractWord(String word) {
return extractWord(word, 0);
}
public void addWord(String word, String... syllables) {
addWord(word, 0, syllables);
}
public void deleteWord(String word) {
deleteWord(word, 0);
}
}
Node
package Tree;
import Exceptions.NoSuchWordException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import static java.lang.Math.min;
public class Node implements Serializable {
private String[] syllables;
private Map<String, Node> heirs = new HashMap<>();
public String getSyllables() {
if (syllables.length == 0)
return "There are no syllables saved for this word!";
StringBuilder strb = new StringBuilder();
for (int i = 0;;) {
strb.append(syllables[i]);
if (++i != syllables.length)
strb.append('-');
else
break;
}
return strb.toString();
}
public void printSyllables() {
System.out.println(getSyllables());
}
public String[] getSyllablesAsArray() {
return syllables;
}
Node extractWord(String word, int pos) {
if (pos == word.length())
return this;
for (Map.Entry<String, Node> entry : heirs.entrySet())
if (compareWords(entry.getKey(), word, pos))
return entry.getValue().extractWord(word, pos + entry.getKey().length());
throw new NoSuchWordException();
}
void addWord(String word, int pos, String... syllables) {
for (Map.Entry<String, Node> entry : heirs.entrySet()) {
int mmlsl = getMaxMatchedLettersSequenceLength(entry.getKey(), word, pos);
if (mmlsl == word.length() - pos && mmlsl == entry.getKey().length()) {
entry.getValue().syllables = syllables;
return;
}
if (mmlsl == entry.getKey().length()) {
entry.getValue().addWord(word, pos + mmlsl, syllables);
return;
}
if (mmlsl == word.length() - pos) {
heirs.remove(entry.getKey());
Node newNode = new Node();
newNode.syllables = syllables;
heirs.put(word.substring(pos), newNode);
newNode.heirs.put(entry.getKey().substring(mmlsl), entry.getValue());
return;
}
if (mmlsl != 0) {
heirs.remove(entry.getKey());
Node intermediateNode = new Node();
heirs.put(entry.getKey().substring(0, mmlsl), intermediateNode);
Node newNode = new Node();
newNode.syllables = syllables;
intermediateNode.heirs.put(word.substring(pos + mmlsl), newNode);
intermediateNode.heirs.put(entry.getKey().substring(mmlsl), entry.getValue());
return;
}
}
Node newNode = new Node();
newNode.syllables = syllables;
heirs.put(word.substring(pos), newNode);
}
void deleteWord(String word, int pos) {
for (Map.Entry<String, Node> entry : heirs.entrySet())
if (compareWords(entry.getKey(), word, pos))
if (pos + entry.getKey().length() == word.length()) {
heirs.remove(entry.getKey());
return;
} else
deleteWord(word, pos + entry.getKey().length());
throw new NoSuchWordException();
}
private boolean compareWords(String s1, String s2, int pos) {
return getMaxMatchedLettersSequenceLength(s1, s2, pos) == s1.length();
}
private int getMaxMatchedLettersSequenceLength(String s1, String s2, int pos) {
int minL = min(s1.length(), s2.length() - pos);
int count = 0;
for (int i = 0; i < minL; i++)
if (s1.charAt(i) == s2.charAt(pos + i))
count++;
else
break;
return count;
}
}
NoSuchWordException
package Exceptions;
public class NoSuchWordException extends IllegalArgumentException {}
P.S.: Это мой первый словарь, так что замечания приветствуются. Давно хотел его написать, да всё повода не было.
P.S.S.: Простите, что без комментариев, лень их писать, хотя мне кажется, что так даже интереснее.
Answered by Имя Фамилия on February 26, 2021
Я взял за основу правила разбиения слова на фонетически слоги отсюда. Для начала класс реализации ленты с указателем + возможность ставить кое-какие маркеры:
package click.webelement;
public class Ribbon {
private int position = -1;
private final int length;
private final String input;
private int flag = -1;
private int startSyllableIndex = 0;
private int endSyllableIndex = 0;
public Ribbon(String input){
this.input = input;
length = input.length();
}
public void setEndSyllableIndex(){
endSyllableIndex = position;
}
public String extractSyllable(){
String result = input.substring(startSyllableIndex, endSyllableIndex + 1);
startSyllableIndex = endSyllableIndex + 1;
flag = position;
endSyllableIndex = 0;
return result;
}
public char readCurrentPosition(){
if (position < 0 || position > length - 1){
throw new IllegalStateException();
}
return input.charAt(position);
}
public void setFlag(){
flag = position;
}
public void rewindToFlag(){
if(flag >= 0){
position = flag;
}
}
public boolean moveHeadForward(){
if(position + 1 < length){
position++;
return true;
}else{
return false;
}
}
}
Затем сам алгортим вместе с тестом:
package click.webelement;
import java.util.ArrayList;
import java.util.List;
public class MainRibbon {
final String vowels = "аеёиоуыюя";
final String nonPairConsonant = "лйрнм";
public static void main(String[] args) {
MainRibbon mainRibbon = new MainRibbon();
System.out.println(mainRibbon.syllables("контразведчик"));
System.out.println(mainRibbon.syllables("длинношеее"));
System.out.println(mainRibbon.syllables("программист"));
System.out.println(mainRibbon.syllables("верноподданный"));
System.out.println(mainRibbon.syllables("пуленепробиваемый"));
System.out.println(mainRibbon.syllables("ёж"));
System.out.println(mainRibbon.syllables("ёжик"));
}
List<String> syllables(String input){
List<String> result = new ArrayList<>();
Ribbon ribbon = new Ribbon(input);
while(ribbon.moveHeadForward()){
ribbon.setFlag();
if(checkVowel(ribbon.readCurrentPosition())){
if(ribbon.moveHeadForward() && ribbon.moveHeadForward()){
if(checkVowel(ribbon.readCurrentPosition())){
ribbon.rewindToFlag();
ribbon.setEndSyllableIndex();
result.add(ribbon.extractSyllable());
continue;
}
}
ribbon.rewindToFlag();
if(ribbon.moveHeadForward() && checkSpecialConsonant(ribbon.readCurrentPosition())){
ribbon.setEndSyllableIndex();
result.add(ribbon.extractSyllable());
continue;
}
ribbon.rewindToFlag();
if(hasMoreVowels(ribbon)){
ribbon.rewindToFlag();
ribbon.setEndSyllableIndex();
result.add(ribbon.extractSyllable());
continue;
}else{
while (ribbon.moveHeadForward());
ribbon.setEndSyllableIndex();
result.add(ribbon.extractSyllable());
}
}
}
return result;
}
public boolean checkVowel(char ch){
return vowels.contains(String.valueOf(ch));
}
public boolean hasMoreVowels(Ribbon ribbon){
while (ribbon.moveHeadForward()){
if(checkVowel(ribbon.readCurrentPosition())){
return true;
}
}
return false;
}
public boolean checkSpecialConsonant(char ch){
return nonPairConsonant.contains(String.valueOf(ch));
}
}
Вывод:
[кон, тра, зве, дчик]
[длин, но, ше, е, е]
[про, грам, мист]
[вер, но, по, ддан, ный]
[пу, ле, не, про, би, ва, е, мый]
[ёж]
[ё, жик]
Answered by Alexey R. on February 26, 2021
Возьму за основу правила из раздела Традиционная школа на сайте slogi.su:
В традиционной школе всё просто. Сколько гласных, столько и слогов, а как ребёнок разделит, не важно (лишь бы красиво звучало и ребёнку было понятно). Как бы школьника ни научили делить на слоги, это нигде не отразится: ни на ОГЭ, ни на ЕГЭ (там таких заданий нет). Перечислим три простых правила школьной программы.
- Слог образует гласный звук:
сте-на, ба-ран (с-тена, бара-н — неправильно).- Слог начинается с согласного, который стоит перед гласной:
мо-ло-ко, ко-ра (мол-око, кор-а — неправильно).- Буквы ь, ъ (которые не означают звуков), й нельзя отрывать от предыдущего слога:
лай-ка, конь-ки, подъ-езд (ла-йка, кон-ьки, под-ъезд — неправильно).В традиционной школе допускается вариативность: со-лнце или сол-нце, ко-мпью-тер или ком-пью-тер.
Но исправлю косяк с буквой Й
(dопрос на rusSE):
Й
в сочетаниях ЙА
, ЙИ
, ЙУ
, ЙЕ
, ЙО
начинает новый слог, а в сочетаниях ЙЯ
, ЙЫ
, ЙЮ
, ЙЭ
, ЙЁ
- нет (точный список сочетаний ещё обсуждается).Для замены и вставки между слогами дефисов регулярка такая:
[ЙЦКНГШЩЗХЪФВПРЛДЖЧСМТЬБ]*[ЁУЕЫАОЭЯИЮ][ЙЦКНГШЩЗХЪФВПРЛДЖЧСМТЬБ]*?(?=[ЦКНГШЩЗХФВПРЛДЖЧСМТБ]?[ЁУЕЫАОЭЯИЮ]|Й[АИУЕО])
Для поиска и выбора всех слогов - немного посложнее:
[ЙЦКНГШЩЗХЪФВПРЛДЖЧСМТЬБ]*[ЁУЕЫАОЭЯИЮ][ЙЦКНГШЩЗХЪФВПРЛДЖЧСМТЬБ]*?(?=$|[^А-ЯЁ]|[ЦКНГШЩЗХФВПРЛДЖЧСМТБ]?[ЁУЕЫАОЭЯИЮ]|Й[АИУЕО])
Для английского могут быть побольше возни с буквой Y
, впрочем и тут с Й
аналогично получилось. Это если не задуматься о том, что у них могут быть другие правила выделения слогов.
На джаве сделаешь сам, а вот пример работы на js:
function log(s) {
console.log(s.replace(/[ЙЦКНГШЩЗХЪФВПРЛДЖЧСМТЬБ]*[ЁУЕЫАОЭЯИЮ][ЙЦКНГШЩЗХЪФВПРЛДЖЧСМТЬБ]*?(?=[ЦКНГШЩЗХФВПРЛДЖЧСМТБ]?[ЁУЕЫАОЭЯИЮ]|Й[АИУЕО])/ig, "$&-"))
console.log((s.match(/[ЙЦКНГШЩЗХЪФВПРЛДЖЧСМТЬБ]*[ЁУЕЫАОЭЯИЮ][ЙЦКНГШЩЗХЪФВПРЛДЖЧСМТЬБ]*?(?=$|[^А-ЯЁ]|[ЦКНГШЩЗХФВПРЛДЖЧСМТБ]?[ЁУЕЫАОЭЯИЮ]|Й[АИУЕО])/ig) || []).join(" "))
}
log("Есть определённое количество слов, около 2000 тыс. Можно ли их как-то программно разбить по слогам?")
log("стена, баран; молоко, кора; лайка, коньки, подъезд")
log("солнце, компьютер; безвозмездно; соловьиный, длинношеее")
log("майор, фойе, папайя, алилуйя, Гийю, тейю, бойи, гайярдия, зухдийят")
.as-console-wrapper.as-console-wrapper { max-height: 100vh }
PS: Для переносов этот алгоритм несколько некорректный - помимо отрезания однобуквенных слогов, он никак не учитывает морфемную структуру слова, которую рекомендуют учитывать при переносах (например, не отрывать одну букву от корня).
PPS: Алгоритм из раздела Школа углублённого изучения тоже при желании можно более-менее реализовать, просто там будет правил больше и придётся все сочетания с изменяющимся звучанием прописывать (типа ть?сяb
).
Answered by Qwertiy on February 26, 2021
Попробуйте воспользоваться сервисом https://slogi.su/ (ни в коем случае не реклама, нашел на просторах гугла). Если посмотреть, то там довольно простой request -> response.
Если смотреть обычным curl'ом, то результат будет следующий
$ curl -d 'q=собака' -X POST 'https://slogi.su/word.json'
{"status":"ok","text":"В слове 3 слога:<br/><span class=word-syllables>со-ба-ка</span><br/><a href='/собака'>Подробнее</a>"}
Далее все это дело надо запрограммировать на java. Код будет примерно следующим:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) throws Exception {
URL url = new URL("https://slogi.su/word.json");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
con.getOutputStream().write("q=собака".getBytes());
StringBuilder content = new StringBuilder();
try (BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()))) {
String line;
while ((line = in.readLine()) != null) {
content.append(line);
}
}
con.disconnect();
String output = content.toString();
System.out.println("Полный ответ : " + output); // а дальше парсите ответ как хотите. Самый простой и наивный вариант - через регулярку
Pattern pattern = Pattern.compile("<span class=word-syllables>(.+)</span>");
Matcher matcher = pattern.matcher(output);
if (matcher.find()) {
System.out.println("Ответ по слогам : " + matcher.group(1));
} else {
System.out.println("Ответ по слогам не найден");
}
}
}
в результате будет такой вывод
Полный ответ : {"status":"ok","text":"В слове 3 слога:<br/><span class=word-syllables>со-ба-ка</span><br/><a href='/собака'>Подробнее</a>"}
Ответ по слогам : со-ба-ка
P.S. не надо придумывать велосипед, который до вас уже придумали, проще воспользоваться готовым решением
Answered by Andrew Bystrov on February 26, 2021
Как вариант: два масива, в одном гласные, в другом согласные буквы. Разбивать по буквенно и проверять. Опять же, продумайте условия, согласно правилам русского языка. Что-то по алгоритму похожу к поиску min max в масиве.
Answered by Happy Hends on February 26, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP