按字順序在多個字元後換行字串
1. 概述
在本教程中,我們將了解如何在給定數量的字元後自動換行。因此,我們的程式將傳回一個帶有新換行符的轉換後的String
。
2. 通用演算法
讓我們考慮以下句子: Baeldung is a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies
。
我們要插入的**行最多返回每n
字符**, n
代表字符數。讓我們看看執行此操作的程式碼:
String wrapStringCharacterWise(String input, int n) {
StringBuilder stringBuilder = new StringBuilder(input);
int index = 0;
while(stringBuilder.length() > index + n) {
index = stringBuilder.lastIndexOf(" ", index + n);
stringBuilder.replace(index, index + 1, "\n");
index++;
}
return stringBuilder.toString();
}
讓我們以n=20
並理解我們的範例程式碼:
- 我們首先找到
20
字元之前的最新空白:在本例中,在單字a
和popular
之間 - 然後我們用換行符號替換這個空格
- 我們從下一個單字的開頭重新開始,在我們的例子中
popular
當剩餘句子少於20
字元時,我們停止演算法。我們自然地透過for
迴圈來實作這個演算法。此外,為了方便起見,我們在內部使用了StringBuilder
,並對我們的輸入進行了參數化:
我們可以編寫一個單元測試來確認我們的方法傳回範例的預期結果:
@Test
void givenStringWithMoreThanNCharacters_whenWrapStringCharacterWise_thenCorrectlyWrapped() {
String input = "Baeldung is a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies.";
assertEquals("Baeldung is a\npopular website that\nprovides in-depth\ntutorials and\narticles on various\nprogramming and\nsoftware development\ntopics, primarily\nfocused on Java and\nrelated\ntechnologies.", wrapper.wrapStringCharacterWise(input, 20));
}
3. 邊緣情況
目前,我們已經編寫了一段非常幼稚的程式碼。在現實生活中,我們可能需要考慮一些邊緣情況。在本文中,我們將討論其中兩個。
3.1.單字長度超過字元限制
首先,如果單字太大而無法換行怎麼辦?為了簡單起見,我們在這種情況下拋出一個IllegalArgumentException
。在循環的每次迭代中,我們需要檢查給定長度之前是否確實存在空格:
String wrapStringCharacterWise(String input, int n) {
StringBuilder stringBuilder = new StringBuilder(input);
int index = 0;
while(stringBuilder.length() > index + n) {
index = stringBuilder.lastIndexOf(" ", index + n);
if (index == -1) {
throw new IllegalArgumentException("impossible to slice " + stringBuilder.substring(0, n));
}
stringBuilder.replace(index, index + 1, "\n");
index++;
}
return stringBuilder.toString();
}
這次,我們可以寫一個簡單的 JUnit 測試來進行驗證:
@Test
void givenStringWithATooLongWord_whenWrapStringCharacterWise_thenThrows() {
String input = "The word straightforward has more than 10 characters";
assertThrows(IllegalArgumentException.class, () -> wrapper.wrapStringCharacterWise(input, 10));
}
3.2.帶有行返回的原始輸入
另一個邊緣情況是當輸入String
內部已經包含行返回字元時。目前,如果我們在句子中的Baeldung
一詞後面加上一行 return,它將以相同的方式換行。然而,在現有行返回後開始換行聽起來更直觀。
因此,我們將在演算法的每次迭代中搜尋最後一行返回;如果存在,我們移動遊標並跳過換行部分:
String wrapStringCharacterWise(String input, int n) {
StringBuilder stringBuilder = new StringBuilder(input);
int index = 0;
while(stringBuilder.length() > index + n) {
int lastLineReturn = stringBuilder.lastIndexOf("\n", index + n);
if (lastLineReturn > index) {
index = lastLineReturn;
} else {
index = stringBuilder.lastIndexOf(" ", index + n);
if (index == -1) {
throw new IllegalArgumentException("impossible to slice " + stringBuilder.substring(0, n));
}
stringBuilder.replace(index, index + 1, "\n");
index++;
}
}
return stringBuilder.toString();
}
同樣,我們可以在範例中測試我們的程式碼:
@Test
void givenStringWithLineReturns_whenWrapStringCharacterWise_thenWrappedAccordingly() {
String input = "Baeldung\nis a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies.";
assertEquals("Baeldung\nis a popular\nwebsite that\nprovides in-depth\ntutorials and\narticles on various\nprogramming and\nsoftware development\ntopics, primarily\nfocused on Java and\nrelated\ntechnologies.", wrapper.wrapStringCharacterWise(input, 20));
}
4. Apache WordUtils的wrap()
方法
我們可以使用Apache WordUtils的wrap()
方法來實作所需的行為。首先,讓我們新增最新的 Apache [commons-text](https://mvnrepository.com/artifact/org.apache.commons/commons-text)
相依性:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.10.0</version>
</dependency>
與我們的程式碼的主要區別在於, wrap()
預設使用與平台無關的System
的行分隔符號:
@Test
void givenStringWithMoreThanNCharacters_whenWrap_thenCorrectlyWrapped() {
String input = "Baeldung is a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies.";
assertEquals("Baeldung is a" + System.lineSeparator() + "popular website that" + System.lineSeparator() + "provides in-depth" + System.lineSeparator() + "tutorials and" + System.lineSeparator() + "articles on various" + System.lineSeparator() + "programming and" + System.lineSeparator() + "software development" + System.lineSeparator() + "topics, primarily" + System.lineSeparator() + "focused on Java and" + System.lineSeparator() + "related" + System.lineSeparator() + "technologies.", WordUtils.wrap(input, 20));
}
預設情況下, wrap()
接受長單字但不換行:
@Test
void givenStringWithATooLongWord_whenWrap_thenLongWordIsNotWrapped() {
String input = "The word straightforward has more than 10 characters";
assertEquals("The word" + System.lineSeparator() + "straightforward" + System.lineSeparator() + "has more" + System.lineSeparator() + "than 10" + System.lineSeparator() + "characters", WordUtils.wrap(input, 10));
}
最後但並非最不重要的一點是,該庫忽略了我們的其他邊緣情況:
@Test
void givenStringWithLineReturns_whenWrap_thenWrappedLikeThereWasNone() {
String input = "Baeldung" + System.lineSeparator() + "is a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies.";
assertEquals("Baeldung" + System.lineSeparator() + "is a" + System.lineSeparator() + "popular website that" + System.lineSeparator() + "provides in-depth" + System.lineSeparator() + "tutorials and" + System.lineSeparator() + "articles on various" + System.lineSeparator() + "programming and" + System.lineSeparator() + "software development" + System.lineSeparator() + "topics, primarily" + System.lineSeparator() + "focused on Java and" + System.lineSeparator() + "related" + System.lineSeparator() + "technologies.", WordUtils.wrap(input, 20));
}
最後,我們可以看一下該方法的重載簽章:
static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords, String wrapOn)
我們注意到附加參數:
-
newLineStr
:使用不同的字元來插入新行 -
wrapLongWords
:一個boolean
,用於決定是否對長字進行換行 -
wrapOn
:可以使用任何正規表示式來代替空格
5. 結論
在本文中,我們看到了一種在給定數量的字元後包裝String
的演算法。我們實現了它並添加了對一些邊緣情況的支援。
最後,我們意識到Apache WordUtils' wrap()
方法是高度可配置的,並且在大多數情況下應該就足夠了。但是,如果我們無法使用外部依賴項或需要特定行為,我們可以使用我們自己的實作。
與往常一樣,程式碼可以在 GitHub 上取得。