I'm working on a program that corrects all punctuation spacing in a String
.
Examples:
// "Hello ,World" -> "Hello, World"
// "9 :00 A .M ." -> "9:00 A.M."
// "can 't even." -> "can't even."
Here's how it works:
My process is to first add a space between all punctuation and then remove any unnecessary spaces. This allows me to tokenize both punctuation and words/numbers correctly.
For example, the acronym String s = " A .M ."
is transformed into "A . M ."
.
When split by a space, it returns the list ["A", ".", "M", "."]
.
I then attempt to "fix" the remaining line spacing in the String
, so the result will become "A.M."
.
Here's an example of what the code looks like for correcting acronyms:
In FixAcronym.java
:
/**
* Fix acronyms.
*/
private static String fix(String line, SubtitleObject so) {
StringBuilder builder = new StringBuilder();
String[] split = so.split(RegexEnum.SPACE, line); // same as line.split(" ") but cached
String prevPrevPrev = null, prevPrev = null, prev = null, current = null;
boolean addSpace;
for (int i = 0; i < split.length; ++i) {
prevPrevPrev = prevPrev;
prevPrev = prev;
prev = current;
current = split[i];
addSpace = true;
if (prevPrevPrev != null && prevPrevPrev.length() == 1
&& Character.isLetter(prevPrevPrev.charAt(0))) {
if (prevPrev.equals(".")) {
if (prev.length() == 1 && Character.isLetter(prev.charAt(0))) {
if (current.equals(".")) {
StringBuilderUtil.deleteSpaceAt(builder, builder.length() - 2);
StringBuilderUtil.deleteOnlyIfSpaceAt(builder, builder.length() - 3);
addSpace = false;
}
}
}
}
if (i > 0 && addSpace) {
builder.append(' ');
}
builder.append(current);
}
return builder.toString();
}
In StringBuilderUtil.java
:
/**
* Delete space at index within StringBuilder.
*/
public static void deleteSpaceAt(StringBuilder builder, int index) {
assert builder.charAt(index) == ' ';
builder.deleteCharAt(index);
}
/**
* Delete only if space at index within StringBuilder.
*/
public static void deleteOnlyIfSpaceAt(StringBuilder builder, int index) {
if (builder.charAt(index) == ' ') {
builder.deleteCharAt(index);
}
}
Here's the problem: Because of all the different grammatical rules, there are a lot of different "fix" methods, and each subsequent "fix" uses the same technique.
line = FixTime.fix(line, this);
line = FixContractions.fix(line, this);
line = FixAcronym.fix(line, this);
// etc...
Each fix
splits the line by a space, iterates through the tokens, deletes the space(s) in the StringBuilder
when necessary, and appends the space and/or token.
The problem with this process is hypothetically I only need to iterate through the split line once to get the corrected String
. This also makes the continuous creations, deletions, and appendings of the StringBuilder
extraneous.
"S . H . I . E . L . D ."
. \$\endgroup\$