ÜbersichtSplit String in natürlicher Sprache bricht
I Strings zu einem Text-to-Speech-Server senden, die eine maximale Länge von 300 Zeichen akzeptiert. Aufgrund der Netzwerklatenz kann es zwischen den einzelnen zurückgesendeten Sprachabschnitten zu Verzögerungen kommen. Deshalb möchte ich die Sprachausgabe möglichst bei den "natürlichsten Pausen" unterbrechen.
Jede Serveranfrage kostet mich Geld, also würde ich idealerweise die längste mögliche Zeichenfolge senden, bis zu den maximal zulässigen Zeichen.
Hier ist meine aktuelle Implementierung:
private static final boolean DEBUG = true;
private static final int MAX_UTTERANCE_LENGTH = 298;
private static final int MIN_UTTERANCE_LENGTH = 200;
private static final String FULL_STOP_SPACE = ". ";
private static final String QUESTION_MARK_SPACE = "? ";
private static final String EXCLAMATION_MARK_SPACE = "! ";
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final String COMMA_SPACE = ", ";
private static final String JUST_A_SPACE = " ";
public static ArrayList<String> splitUtteranceNaturalBreaks(String utterance) {
final long then = System.nanoTime();
final ArrayList<String> speakableUtterances = new ArrayList<String>();
int splitLocation = 0;
String success = null;
while (utterance.length() > MAX_UTTERANCE_LENGTH) {
splitLocation = utterance.lastIndexOf(FULL_STOP_SPACE, MAX_UTTERANCE_LENGTH);
if (DEBUG) {
System.out.println("(0 FULL STOP) - last index at: " + splitLocation);
}
if (splitLocation < MIN_UTTERANCE_LENGTH) {
if (DEBUG) {
System.out.println("(1 FULL STOP) - NOT_OK");
}
splitLocation = utterance.lastIndexOf(QUESTION_MARK_SPACE, MAX_UTTERANCE_LENGTH);
if (DEBUG) {
System.out.println("(1 QUESTION MARK) - last index at: " + splitLocation);
}
if (splitLocation < MIN_UTTERANCE_LENGTH) {
if (DEBUG) {
System.out.println("(2 QUESTION MARK) - NOT_OK");
}
splitLocation = utterance.lastIndexOf(EXCLAMATION_MARK_SPACE, MAX_UTTERANCE_LENGTH);
if (DEBUG) {
System.out.println("(2 EXCLAMATION MARK) - last index at: " + splitLocation);
}
if (splitLocation < MIN_UTTERANCE_LENGTH) {
if (DEBUG) {
System.out.println("(3 EXCLAMATION MARK) - NOT_OK");
}
splitLocation = utterance.lastIndexOf(LINE_SEPARATOR, MAX_UTTERANCE_LENGTH);
if (DEBUG) {
System.out.println("(3 SEPARATOR) - last index at: " + splitLocation);
}
if (splitLocation < MIN_UTTERANCE_LENGTH) {
if (DEBUG) {
System.out.println("(4 SEPARATOR) - NOT_OK");
}
splitLocation = utterance.lastIndexOf(COMMA_SPACE, MAX_UTTERANCE_LENGTH);
if (DEBUG) {
System.out.println("(4 COMMA) - last index at: " + splitLocation);
}
if (splitLocation < MIN_UTTERANCE_LENGTH) {
if (DEBUG) {
System.out.println("(5 COMMA) - NOT_OK");
}
splitLocation = utterance.lastIndexOf(JUST_A_SPACE, MAX_UTTERANCE_LENGTH);
if (DEBUG) {
System.out.println("(5 SPACE) - last index at: " + splitLocation);
}
if (splitLocation < MIN_UTTERANCE_LENGTH) {
if (DEBUG) {
System.out.println("(6 SPACE) - NOT_OK");
}
splitLocation = MAX_UTTERANCE_LENGTH;
if (DEBUG) {
System.out.println("(6 MAX_UTTERANCE_LENGTH) - last index at: " + splitLocation);
}
} else {
if (DEBUG) {
System.out.println("Accepted");
}
splitLocation -= 1;
}
}
} else {
if (DEBUG) {
System.out.println("Accepted");
}
splitLocation -= 1;
}
} else {
if (DEBUG) {
System.out.println("Accepted");
}
}
} else {
if (DEBUG) {
System.out.println("Accepted");
}
}
} else {
if (DEBUG) {
System.out.println("Accepted");
}
}
success = utterance.substring(0, (splitLocation + 2));
speakableUtterances.add(success.trim());
if (DEBUG) {
System.out.println("Split - Length: " + success.length() + " -:- " + success);
System.out.println("------------------------------");
}
utterance = utterance.substring((splitLocation + 2)).trim();
}
speakableUtterances.add(utterance);
if (DEBUG) {
System.out.println("Split - Length: " + utterance.length() + " -:- " + utterance);
final long now = System.nanoTime();
final long elapsed = now - then;
System.out.println("ELAPSED: " + TimeUnit.MILLISECONDS.convert(elapsed, TimeUnit.NANOSECONDS));
}
return speakableUtterances;
}
Es ist hässlich, da auf sie nicht in der Lage regex innerhalb lastIndexOf
zu verwenden. Hässlich beiseite, es ist eigentlich ziemlich schnell.
Probleme
Im Idealfall würde Ich mag Regex zu verwenden, die auf einem meiner ersten Wahl Trennzeichen für ein Spiel erlaubt:
private static final String firstChoice = "[.!?" + LINE_SEPARATOR + "]\\s+";
private static final Pattern pFirstChoice = Pattern.compile(firstChoice);
Und dann eine Matcher verwenden, um die Position zu beheben:
Matcher matcher = pFirstChoice.matcher(input);
if (matcher.find()) {
splitLocation = matcher.start();
}
Meine Alternative in meiner aktuellen Implementierung ist, den Speicherort jedes Trennzeichens zu speichern und dann das nächste auszuwählen MAX_UTTERANCE_LENGTH
Ich habe verschiedene Methoden versucht, die MIN_UTTERANCE_LENGTH
& MAX_UTTERANCE_LENGTH
auf das Muster anwenden, so fängt es nur zwischen diesen Werten und lookarounds mit Iterierte umkehren ?<=
, aber das ist, wo mein Wissen beginnt mich zu scheitern:
private static final String poorEffort = "([.!?]{200, 298})\\s+");
Schließlich
ich frage mich, ob jemand von euch kann regex Meister erreichen, was ich nach und con fest, wenn es sich tatsächlich als effizienter erweist?
Ich danke Ihnen im Voraus.
Referenzen:
- Split a string at a natural break (Python)
- Lookarounds
- Regex to Split Tokens With Minimum Size and Delimiters
Wow, große Frage. Ich hoffe, dass einige Antworten bald kommen werden, da dies genau die Art von Herausforderung zu sein scheint, die die 'regex'-Tag-Jungs lieben. – MattSizzle