This commit is contained in:
Looly
2021-10-09 21:47:54 +08:00
parent 412160ac53
commit 3037cb222c
3 changed files with 50 additions and 17 deletions

View File

@@ -3,7 +3,7 @@
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.7.14 (2021-10-08) # 5.7.14 (2021-10-09)
### 🐣新特性 ### 🐣新特性
* 【extra 】 修复HttpCookie设置cookies的方法不符合RFC6265规范问题issue#I4B70D@Gitee * 【extra 】 修复HttpCookie设置cookies的方法不符合RFC6265规范问题issue#I4B70D@Gitee
@@ -16,13 +16,13 @@
* 【core 】 DateTime构造和DateUtil.parse可选是否宽松模式issue#1849@Github * 【core 】 DateTime构造和DateUtil.parse可选是否宽松模式issue#1849@Github
* 【core 】 TreeBuilder增加部分根节点set方法issue#1848@Github * 【core 】 TreeBuilder增加部分根节点set方法issue#1848@Github
* 【core 】 优化Base64.isBase64方法减少一次多余的判断pr#1860@Github * 【core 】 优化Base64.isBase64方法减少一次多余的判断pr#1860@Github
* 【core 】 优化Base64.isBase64方法减少一次多余的判断pr#1860@Github
* 【cache 】 优化FIFOCache未设置过期策略时无需遍历判断过期对象pr#425@Gitee * 【cache 】 优化FIFOCache未设置过期策略时无需遍历判断过期对象pr#425@Gitee
* 【core 】 增加Opt类pr#426@Gitee * 【core 】 增加Opt类pr#426@Gitee
* 【core 】 Week增加of重载支持DayOfWekpr#1872@Github * 【core 】 Week增加of重载支持DayOfWekpr#1872@Github
* 【poi 】 优化read避免多次创建CopyOptionsissue#1875@Github * 【poi 】 优化read避免多次创建CopyOptionsissue#1875@Github
* 【core 】 优化CsvReader实现可控遍历pr#1873@Github * 【core 】 优化CsvReader实现可控遍历pr#1873@Github
* 【core 】 优化Base64.isBase64判断pr#1879@Github * 【core 】 优化Base64.isBase64判断pr#1879@Github
* 【core 】 新增StrFormatter.formatWithpr#430@Gitee
### 🐞Bug修复 ### 🐞Bug修复
* 【http 】 修复HttpCookie设置cookies的方法不符合RFC6265规范问题pr#418@Gitee * 【http 】 修复HttpCookie设置cookies的方法不符合RFC6265规范问题pr#418@Gitee

View File

@@ -9,7 +9,6 @@ import java.util.Map;
* 字符串格式化工具 * 字符串格式化工具
* *
* @author Looly * @author Looly
*
*/ */
public class StrFormatter { public class StrFormatter {
@@ -23,22 +22,42 @@ public class StrFormatter {
* 转义\ format("this is \\\\{} for {}", "a", "b") =》 this is \a for b<br> * 转义\ format("this is \\\\{} for {}", "a", "b") =》 this is \a for b<br>
* *
* @param strPattern 字符串模板 * @param strPattern 字符串模板
* @param argArray 参数列表 * @param argArray 参数列表
* @return 结果 * @return 结果
*/ */
public static String format(final String strPattern, final Object... argArray) { public static String format(String strPattern, Object... argArray) {
if (StrUtil.isBlank(strPattern) || ArrayUtil.isEmpty(argArray)) { return formatWith(strPattern, StrUtil.EMPTY_JSON, argArray);
}
/**
* 格式化字符串<br>
* 此方法只是简单将指定占位符 按照顺序替换为参数<br>
* 如果想输出占位符使用 \\转义即可,如果想输出占位符之前的 \ 使用双转义符 \\\\ 即可<br>
* 例:<br>
* 通常使用format("this is {} for {}", "{}", "a", "b") =》 this is a for b<br>
* 转义{} format("this is \\{} for {}", "{}", "a", "b") =》 this is \{} for a<br>
* 转义\ format("this is \\\\{} for {}", "{}", "a", "b") =》 this is \a for b<br>
*
* @param strPattern 字符串模板
* @param placeHolder 占位符,例如{}
* @param argArray 参数列表
* @return 结果
* @since 5.7.14
*/
public static String formatWith(String strPattern, String placeHolder, Object... argArray) {
if (StrUtil.isBlank(strPattern) || StrUtil.isBlank(placeHolder) || ArrayUtil.isEmpty(argArray)) {
return strPattern; return strPattern;
} }
final int strPatternLength = strPattern.length(); final int strPatternLength = strPattern.length();
final int placeHolderLength = placeHolder.length();
// 初始化定义好的长度以获得更好的性能 // 初始化定义好的长度以获得更好的性能
StringBuilder sbuf = new StringBuilder(strPatternLength + 50); final StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
int handledPosition = 0;// 记录已经处理到的位置 int handledPosition = 0;// 记录已经处理到的位置
int delimIndex;// 占位符所在位置 int delimIndex;// 占位符所在位置
for (int argIndex = 0; argIndex < argArray.length; argIndex++) { for (int argIndex = 0; argIndex < argArray.length; argIndex++) {
delimIndex = strPattern.indexOf(StrUtil.EMPTY_JSON, handledPosition); delimIndex = strPattern.indexOf(placeHolder, handledPosition);
if (delimIndex == -1) {// 剩余部分无占位符 if (delimIndex == -1) {// 剩余部分无占位符
if (handledPosition == 0) { // 不带占位符的模板直接返回 if (handledPosition == 0) { // 不带占位符的模板直接返回
return strPattern; return strPattern;
@@ -54,24 +73,23 @@ public class StrFormatter {
// 转义符之前还有一个转义符,占位符依旧有效 // 转义符之前还有一个转义符,占位符依旧有效
sbuf.append(strPattern, handledPosition, delimIndex - 1); sbuf.append(strPattern, handledPosition, delimIndex - 1);
sbuf.append(StrUtil.utf8Str(argArray[argIndex])); sbuf.append(StrUtil.utf8Str(argArray[argIndex]));
handledPosition = delimIndex + 2; handledPosition = delimIndex + placeHolderLength;
} else { } else {
// 占位符被转义 // 占位符被转义
argIndex--; argIndex--;
sbuf.append(strPattern, handledPosition, delimIndex - 1); sbuf.append(strPattern, handledPosition, delimIndex - 1);
sbuf.append(StrUtil.C_DELIM_START); sbuf.append(placeHolder.charAt(0));
handledPosition = delimIndex + 1; handledPosition = delimIndex + 1;
} }
} else {// 正常占位符 } else {// 正常占位符
sbuf.append(strPattern, handledPosition, delimIndex); sbuf.append(strPattern, handledPosition, delimIndex);
sbuf.append(StrUtil.utf8Str(argArray[argIndex])); sbuf.append(StrUtil.utf8Str(argArray[argIndex]));
handledPosition = delimIndex + 2; handledPosition = delimIndex + placeHolderLength;
} }
} }
// append the characters following the last {} pair.
// 加入最后一个占位符后所有的字符 // 加入最后一个占位符后所有的字符
sbuf.append(strPattern, handledPosition, strPattern.length()); sbuf.append(strPattern, handledPosition, strPatternLength);
return sbuf.toString(); return sbuf.toString();
} }

View File

@@ -6,19 +6,34 @@ import org.junit.Test;
import cn.hutool.core.text.StrFormatter; import cn.hutool.core.text.StrFormatter;
public class StrFormatterTest { public class StrFormatterTest {
@Test @Test
public void formatTest(){ public void formatTest() {
//通常使用 //通常使用
String result1 = StrFormatter.format("this is {} for {}", "a", "b"); String result1 = StrFormatter.format("this is {} for {}", "a", "b");
Assert.assertEquals("this is a for b", result1); Assert.assertEquals("this is a for b", result1);
//转义{} //转义{}
String result2 = StrFormatter.format("this is \\{} for {}", "a", "b"); String result2 = StrFormatter.format("this is \\{} for {}", "a", "b");
Assert.assertEquals("this is {} for a", result2); Assert.assertEquals("this is {} for a", result2);
//转义\ //转义\
String result3 = StrFormatter.format("this is \\\\{} for {}", "a", "b"); String result3 = StrFormatter.format("this is \\\\{} for {}", "a", "b");
Assert.assertEquals("this is \\a for b", result3); Assert.assertEquals("this is \\a for b", result3);
} }
@Test
public void formatWithTest() {
//通常使用
String result1 = StrFormatter.formatWith("this is ? for ?", "?", "a", "b");
Assert.assertEquals("this is a for b", result1);
//转义?
String result2 = StrFormatter.formatWith("this is \\? for ?", "?", "a", "b");
Assert.assertEquals("this is ? for a", result2);
//转义\
String result3 = StrFormatter.formatWith("this is \\\\? for ?", "?", "a", "b");
Assert.assertEquals("this is \\a for b", result3);
}
} }