package cn.jeesoft.core.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* JDiy字符串操作工具类.
*
* @author 子秋(ziquee) http://www.jdiy.org
*/
public final class Txt {
private String str;
/**
* 创建一个新的Txt字符串处理对象.
* 可以使用该对象处理与正则表达式相关的操作.
*
* @param str 要被处理的字符串.
*/
public Txt(String str) {
this.str = str;
}
/**
* 返回字符串dest在字符串source中出现的次数.要查找的字符串是区分大小写的.
* @param source 原字符串.
* @param dest 要搜索的字符串.
* @return 字符串dest在字符串source中出现的次数.
*/
public static int containSum(String source, String dest) {
//这是一个很好的字符串查找算法: 查找dest在source中出现的次数
return (source.length() - new Txt(source).restr(dest, "").toString().length()) / dest.length();
}
/**
* 截字符串前面的指定数量个字符.
* 如果字节符度超过该数量,则后面用省略号代替(因为省略号会占去两个字符,因此当有省略号时,实际显示的字符数=toCount-2).
*
* @param str 要截取取的字符串。
* @param toCount 要截取的字符数(一个汉字计两个字符)
* @return 截取后的新字符串。
*/
public static String cut(String str, int toCount) {
try {
StringBuilder reStr = new StringBuilder("");
char[] tempChar = str.toCharArray();
for (int i = 0; i < tempChar.length; i++) {
byte[] b = String.valueOf(tempChar[i]).getBytes("utf-8");
toCount -= b.length > 1 ? 2 : 1;
if (toCount <= 0) {
if (i == tempChar.length - 1) {
reStr.append(tempChar[i]);
} else {
reStr.append("…");
break;
}
} else {
reStr.append(tempChar[i]);
}
}
return reStr.toString();
} catch (java.io.UnsupportedEncodingException ex) {
return str;
}
}
/**
* 这是一个字符串注入(或叫做字符串模板替换)方法.
* 在JDiy日志管理中大量应用了此方法.
* 首先定义一个带有一个或多个"{}"占位符的字符串模版,然后占位符处的内容会由后面的参数依次注入替换.
* <br /><strong>下面是一个示例性的代码:</strong><br/>
* String log = "对不起,要更新的字段出错。字段名:{}, 字段值:{}, 错误原因:{}";<br />
* String log2 = Txt.inject(log, "name", "ziquee", "无此字段");<br />
* //执行结果如下:<br />
* log2="对不起,要更新的字段出错。字段名:name, 字段值:ziquee, 错误原因:无此字段";
* @param inputString 原始字符串模板.
* @param injects 注入进去的字符串列表.
* @return 执行注入后的字符串.
* @see #injectBy(String, String, String...)
*/
public static String inject(String inputString, String... injects) {
return injectBy(inputString, "{}", injects);
}
/**
* 这是一个字符串注入(或叫做字符串模板替换)方法.
* 与{@link #inject(String, String...)}不同的是,此方法可以自已定义占位符.
* <br /><strong>下面是一个示例性的代码:</strong><br/>
* String log = "对不起,要更新的字段出错。字段名:?, 字段值:?, 错误原因:?";<br />
* String log2 = Txt.injectBy(log, "?", "name", "ziquee", "无此字段");<br />
* //执行结果如下:<br />
* log2="对不起,要更新的字段出错。字段名:name, 字段值:ziquee, 错误原因:无此字段";
* @param inputString 原始字符串模板.
* @param injectString 字符串占位符.
* @param injects 注入进去的字符串列表.
* @return 执行注入后的字符串.
* @see #inject(String, String...)
*/
public static String injectBy(String inputString, String injectString, String... injects) {
if (inputString == null || injects == null || injects.length == 0) return inputString;
if (injectString == null || "".equals(injectString)) injectString = "{}";
StringBuilder sb = new StringBuilder();
int begin=0, replaced=0, len =injectString.length(), len1=injects.length;
while (true) {
int sub = inputString.indexOf(injectString, begin);
if(sub!=-1){
if(replaced<len1){
sb.append(inputString.substring(begin, sub)).append(injects[replaced++]);
begin = sub + len;
}else{
break;
}
}else{
break;
}
}
sb.append(inputString.substring(begin));
return sb.toString();
}
/**
* 将字符串数组元素按指定的字符串分隔符连成一个新的字符串。
*
* @param arr 要连接的字符串数组.
* @param joinStr 连接字符串. 如果此字符串为空(null),则使用逗号加一个空格(, )进行连接。
* @return 返回arr的每个下标元素按joinStr连接成的新的字符串. 如果数组arr为空(null),则返回结果也将为null.
*/
public static String join(String[] arr, String joinStr) {
if (null == arr) return null;
if (null == joinStr) joinStr = ", ";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
if (i > 0) sb.append(joinStr);
sb.append(arr[i]);
}
return sb.toString();
}
/**
* 将字符串按指定的分隔符分割成为一个数组. <br />
* 与String.split(String s)、StringTokenizer不同的是,此方法将比它们更加高效快速.
* 在需要将字符串split转换为数组时,应该优先使用此方法.
* @param str 要转换的字符串.
* @param sp 分隔字符串.
* @return 转换后的String数组.
* @since JDiy-1.6 及以上版本新增的方法.
*/
public static String[] split(String str, String sp) {
List<String> arrayList = new ArrayList<String>();
int index = 0, offset = 0, len = sp.length();
while ((index = str.indexOf(sp, index + len)) != -1) {
arrayList.add(str.substring(offset, index));
offset = index + len;
}
if(offset<str.length()) arrayList.add(str.substring(offset));
return arrayList.toArray(new String[arrayList.size()]);
}
/**
* 此方法将HTML内容转换为普通文本. <br/>
* 此方法将通过对正则替换,删除inputString中的HTML标签,并返回纯文本内容.
* @param inputString 要转换的HTML代码.
* @return 转换后的纯文本内容.
* @since 这是JDiy-2.1及后续版本新增的方法.
*/
public static String htmlToText(String inputString) {
String htmlStr = inputString;
String textStr = "";
String scriptRegEx = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>";
String styleRegEx = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>";
String htmlRegEx1 = "<[^>]*>";
String htmlRegEx2 = "<[^>]*";
try {
Pattern scriptPattern = Pattern.compile(scriptRegEx, Pattern.CASE_INSENSITIVE);
Matcher scriptMatcher = scriptPattern.matcher(htmlStr);
htmlStr = scriptMatcher.replaceAll("");
Pattern stylePattern = Pattern.compile(styleRegEx, Pattern.CASE_INSENSITIVE);
Matcher styleMatcher = stylePattern.matcher(htmlStr);
htmlStr = styleMatcher.replaceAll("");
Pattern htmlPattern1 = Pattern.compile(htmlRegEx1, Pattern.CASE_INSENSITIVE);
Matcher htmlMatcher1 = htmlPattern1.matcher(htmlStr);
htmlStr = htmlMatcher1.replaceAll("");
Pattern htmlPattern2 = Pattern.compile(htmlRegEx2, Pattern.CASE_INSENSITIVE);
Matcher htmlMatcher2 = htmlPattern2.matcher(htmlStr);
htmlStr = htmlMatcher2.replaceAll("");
textStr = htmlStr;
} catch (Exception e) {
System.err.println("->Txt.htmlToText(String inputString) ERROR:" + e.getMessage());
}
textStr = textStr.replaceAll("´", "\'");
textStr = textStr.replaceAll(""", "\"");
textStr = textStr.replaceAll("<", "<");
textStr = textStr.replaceAll(">", ">");
textStr = textStr.replaceAll(" ", " ");
textStr = textStr.replaceAll("&", "&");
return textStr;
}
/**
* 将指定的字符串内容进行HTML标签转换过滤. <br />
* 此方法会将内容中的HTML标签转换成普通字符输出以防止非法的HTML代码被执行; 同时此方法自动识别URL地址、邮件地址并为其添加链接.<br />
* <strong>注意:</strong> 由在线WEB编辑器添加的内容,不应调用此方法,否则编辑器上传的所有HTML代码将被转换成普通字符输出。
*
* @param str 要进行HTML标签过滤处理处理的原字符串。
* @return 转换之后的字符串。
*/
@SuppressWarnings("unused")
public static String encodeHTML(String str) {
String s = str.replaceAll("<", "<").replaceAll(">", ">")
.replaceAll("\"", """).replaceAll("'", "´ ")
.replaceAll(" ", " ")
.replaceAll("\\r\\n", "<br/>")
.replaceAll("\\n", "<br/>");
Txt rep = new Txt(s);
rep.replace("http://([%#=&\\?\\./a-zA-Z0-9]+)", "<a href=\"http://$1\" target=\"_blank\">http://$1</a>");
rep.replace("([-_a-z0-9]+?)@([-_\\.a-zA-Z0-9]{5,})", "<a href=\"mailto:$1@$2\" target=\"_blank\">$1@$2</a>");
return rep.toString();
}
/**
* 将给定的字符串s转化为一个双精度浮点数. 如果s不能转化, 则返回0.0d.
* 此方法与Javascript脚本的parseFloat执行方式类似.
* 即程序通过给定的字符串,从左往右匹配截取数字字符,并将其转化为double类型后返回.
*
* @param s 要被转化的字符串
* @return 一个有效的双精度浮点数.
* @see #parseDouble(String, double)
*/
public static double parseDouble(String s) {
return parseDouble(s, 0.0d);
}
/**
* 将给定的字符串s转化为一个双精度浮点数. 如果s不能转化, 则返回defV
*
* @param s 要被转化的字符串
* @param defV 当指定的字符串不能转换时,要返回的默认值.
* @return 一个有效的双精度浮点数.
* @see #parseDouble(String)
*/
public static double parseDouble(String s, double defV) {
try {
return Double.parseDouble(new Txt(s).replace("^(\\d+)(\\.*)(\\d*)(.|\r|\n)*$", "$1$2$3").toString());
} catch (Exception e) {
return defV;
}
}
/**
* 将给定的字符串s转化为一个整数. 如果s不能转化为一个整数,则返回0.
* 此方法与Javascript脚本的parseInt执行方式类似.
* 即程序通过给定的字符串,从左往右匹配截取数字字符,并将其转化为整数类型后返回.
* @param s 要被转化的字符串
* @return 一个有效的整数.
* @see #parseInt(String, int)
*/
public static int parseInt(String s) {
return parseInt(s, 0);
}
/**
* 将给定的字符串s转化为一个整数. 如果s不能转化为一个整数,则返回defV
*
* @param s 要被转化的字符串
* @param defV 如果字符串不能被转化成一个有效的整数,程序将返回defV.
* @return 一个有效的整数.
* @see #parseInt(String)
*/
public static int parseInt(String s, int defV) {
try {
return Integer.parseInt(new Txt(s).replace("(\\d+)(.|\r|\n)*$", "$1").toString());
} catch (Exception e) {
return defV;
}
}
/**
* 将给定的字符串s转化为一个长整数. 如果s不能转化为一个长整数,则返回0L.
*<br/>程序将通过给定的字符串,从左往右匹配截取数字字符,并将其转化为长整数类型后返回.
*
* @param s 要被转化的字符串
* @return 一个有效的长整数.
* @see #parseLong(String, long)
*/
public static long parseLong(String s) {
return parseLong(s, 0L);
}
/**
* 将给定的字符串s转化为一个长整数. 如果s不能转化为一个长整数,则返回defV.
*
* @param s 要被转化的字符串.
* @param defV 如果字符串不能被转化成一个有效的长整数,程序将返回defV.
* @return 一个有效的长整数
* @see #parseLong(String)
*/
public static long parseLong(String s, long defV) {
try {
return Long.parseLong(new Txt(s).replace("(\\d+)(.|\r|\n)*$", "$1").toString());
} catch (Exception e) {
return defV;
}
}
/**
* 测试字符串是否能够匹配该正则模式(完全匹配).
*
* @param regStr 要匹配的正则模式.
* 由于此方法为完全匹配,因此regStr正则模式往往以^开头,以$结尾.
* @return 当匹配成功时返回true, 否则返回false.
* @see #find(String)
*/
public boolean test(String regStr) {
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(str);
return matcher.matches();
}
/**
* 测试字符串是否包含该正则模式.
*
* @param regStr 要匹配的正则模式
* @return 当找到匹配时返回true, 否则返回false
* @see #test(String)
*/
public boolean find(String regStr) {
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(str);
return matcher.find();
}
/**
* 将字符串中符合regStr正则模式的字符串替换为s1. 此操作将替换所有符合正则匹配条件的字符串.
* 此方法实现的功能与String对象的replaceAll(String, String)方法类似.
*
* @param regStr 用于匹配的正则表达式.
* @param s1 要替换符合匹配内容的字符串.
* @return 当前的Txt对象.
* @see #replaceFirst(String, String)
* @see #restr(String, String)
*/
public Txt replace(String regStr, String s1) {
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(str);
str = matcher.replaceAll(s1);
return this;
}
/**
* 将字符串中符合正则匹配的字符串替换为s1. 此操作将仅替换一次符合正则匹配条件的字符串.
* 此方法实现的功能与String对象的replaceFirst(String, String)方法类似.
*
* @param regStr 用于匹配的正则表达式
* @param s1 要替换符合匹配内容的字符串.
* @return 当前的Txt对象.
* @see #replace(String, String)
* @see #restr(String, String)
*/
@SuppressWarnings("unused")
public Txt replaceFirst(String regStr, String s1) {
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(str);
str = matcher.replaceFirst(s1);
return this;
}
/**
* 将字符串中的s1替换成s2. 与{@link #replace(String, String)}方法不同的是,此方法仅限普通字符串替换,而不使用正则表达式。
*
* @param s1 要替换的普通字符串.
* @param s2 要替换成的字符串
* @return 当前的Txt对象.
* @see #replace(String, String)
* @since 此方法为JDiy-1.9 及后续版本新增的功能.
*/
public Txt restr(String s1, String s2) {
StringBuilder sb = new StringBuilder();
int b, e = 0, len = s1.length();
while ((b = str.indexOf(s1, e)) != -1) {
sb.append(str.substring(e, b));
sb.append(s2);
e = b + len;
}
if (e < str.length()) sb.append(str.substring(e));
str = sb.toString();
return this;
}
/**
* 返回处理之后的字符串.
* @return 处理之后的字符串.
*/
@Override
public String toString() {
return str;
}
}