1. 内建函数就像FreeMarker在对象中添加的方法一样。要防止和实际方法和其它子变量的命名冲突, 则不能使用点(.), 这里使用问号(?)来和父对象分隔开。
2. 为了简洁, 如果方法没有参数, 那么就可以忽略(), 比如: 想要获取path的长度, 就可以写作: path?length, 而不是path?length()。
3. 内建函数关键性的另外一个原因是FreeMarker不会暴露对象的Java API。尽管Java的String类有length()方法, 但在模板中却是不可见的, 就不得不使用path?length来代替。这里的优点是模板不依赖下层Java对象的精确类型。
4. boolean
4.1. 字符串转为布尔值。字符串必须是true或false, 或者必须是由boolean_format设置的特定格式。
4.2. 如果字符串不是适当的格式, 那么当访问该内建函数时, 就会发成错误终止模板处理。
4.3. boolean_format的默认字符串是"true,false"。
4.4. FreeMarker还自带了一个boolean_format是c的值。
4.5. boolean_format用户还可以自定义, 使用逗号分割真和假的值。
5. cap_first
5.1. 字符串中的首单词的首字母大写。
6. uncap_first
6.1. 和cap_first相反。字符串中首单词的首字母小写。
7. capitalize
7.1. 字符串中所有单词的首字母大写。
8. upper_case
8.1. 字符串的大写形式。
9. lower_case
9.1. 字符串的小写形式。
10. contains
10.1. 如果函数中的参数指定的子串出现在源字符串中, 那么返回true。
11. starts_with
11.1. 字符串是否以指定的子字符串开头, 返回布尔值。
12. ends_with
12.1. 字符串是否以指定的子字符串结尾, 返回布尔值。
13. ensure_starts_with
13.1. 如果字符串没有以第一个参数指定的子串开头, 那么就会将它加到字符串开头, 否则返回原字符串。
13.2. 如果指定两个参数, 那么第一个参数就被解释成Java正则表达式, 如果它不匹配字符串的开头, 那么第二个参数指定的字符串就会添加到字符串开头。
13.3. 该方法也接受第三个标志位参数。"r"查找的子串是正则表达式。i比较是大小写不敏感的。值得注意的一点是当不需要第一参数被解释成正则表达式, 而只是普通文本, 但是又想让比较是大小写不敏感的, 那么此时就需要使用"i"作为第三个参数。
14. ensure_ends_with
14.1. 如果字符串没有以第一个参数指定的子串结尾, 那么就会将它加到字符串后面, 否则返回原字符串。
15. index_of
15.1. 返回第一次字符串中出现子串时的索引位置。不要忘了第一个字符的索引是0。
15.2. 你可以使用第二个参数指定开始搜索的索引位置。
15.3. 第二个参数的数值没有限制。如果它是负数, 那就和是0是相同效果了; 如果它比字符串的长度还大, 那么就和它是字符串长度那个数值是一个效果。小数会被切成整数。
16. last_index_of
16.1. 返回最后一次(最右边)字符串中出现子串时的索引位置。它返回子串第一个(最左边)字符所在位置的索引。
16.2. 可以使用第二个参数来指定开始搜索的索引。要注意第二个参数暗示了子串开始的最大索引。
16.3. 对第二个参数的数值没有限制: 如果它是负数, 会始终返回-1; 如果它比字符串的长度还大, 那么就和它是字符串长度那个数值是一个效果。小数会被切成整数。
16.4. 如果第一个参数作为子串没有在该字符串中出现时(如果你使用了第二个参数, 那么就从给定的序列开始), 那么就返回-1。
17. html/xhtml
17.1. 字符串按照html标记输出。也就是说, 下面字符串将会被替代:
- < 替换为 <
- > 替换为 >
- & 替换为 &
- " 替换为 "
- ' 替换为 '
18. keep_before
18.1. 移除字符串的一部分, 该部分是从给定子串开始的部分。
18.2. 如果参数字符串没有找到, 它会返回空串。如果参数是长度为0的字符串, 它会返回源字符串, 不会改变。
18.3. 该方法接受可选的标志位参数, 作为它的第二个参数。
19. keep_after
19.1. 移除字符串中的一部分内容, 该部分是给定子串第一次出现之前的部分。
19.2. 如果参数字符串没有找到, 它会返回空串。如果参数是长度为0的字符串, 它会返回源字符串, 不会改变。
19.3. 该方法接受可选的标志位参数, 作为它的第二个参数。
20. keep_before_last
20.1. 和keep_before相同, 但是保留参数最后一次出现之前的部分, 而不是第一次出现之前。
21. keep_after_last
21.1. 和keep_after相同, 但是它会保留参数最后一次出现后的部分, 而不是第一次。
22. left_pad
22.1. 如果它仅仅用1个参数, 那么它将在字符串的开始插入空白, 直到整个串的长度达到参数指定的值。如果字符串的长度达到指定数值或者比指定的长度还长, 那么就什么都不做了。
22.2. 如果使用了两个参数, 那么第一个参数表示的含义和你使用一个参数时的相同, 第二个参数指定用什么东西来代替空白字符。
22.3. 第二个参数也可以是个长度比1大的字符串。那么这个字符串会周期性的插入。
22.4. 第二个参数必须是个字符串值, 而且至少有一个字符。
23. right_pad
23.1. 它和left_pad 相同, 但是它从末尾开始插入字符而不是从开头。
24. length
24.1. 字符串中字符的数量。
25. number
25.1. 字符串转化为数字格式。这个数字必须是"计算机语言"格式。也就是说, 它必须是本地化独立的形式, 小数的分隔符就是一个点, 没有分组。
25.2. 此外, 它也识别科学记数法(比如 "1.23E6", "1.5e-8") 。
26. replace
26.1. 在源字符串中, 用另外一个字符串来替换原字符串中出现它的部分。它不处理词的边界。
26.2. 替换是从左向右执行的。比如: ${"aaaaa"?replace("aaa", "X")}, 将会输出Xaa。
26.3. 如果第一个参数是空字符串, 那么所有的空字符串将会被替换, 比如: "foo"?replace("","|"), 就会得到 "|f|o|o|"。
26.4. replace 接受可选的 标志位参数, 作为它的第三参数。
27. remove_beginning
27.1. 从字符串的开头移除参数中的子串, 如果它不以参数中的子串开头, 那么就或者返回原字符串。
28. remove_ending
28.1. 从字符串的结尾移除参数中的子串, 如果它不以参数中的子串结尾, 那么就或者返回原字符串。
29. split
29.1. 它被用来根据另外一个字符串的出现将原字符串分割成字符串序列。
30. trim
30.1. 去掉字符串首尾的空格。
30. url
30.1. 在URL之后的字符串进行转义。这意味着, 所有非US-ASCII的字符和保留的URL字符将会被%XX形式转义。
30.2. 请注意, 它会转义所有保留的URL字符(/, =, &, 等...), 所以可以被用来编码查询参数值。
30.3. 为了进行URL转义, 必须要选择字符集, 它被用来计算被转义的部分(%XX)。
30.4. 如果内建函数url没有参数, 那么它会使用由url_escaping_charset设置的字符集。这个设置应该被软件设置, 包括FreeMarker(比如Web应用框架), 因为它默认不会被设置(null)。 如果它没有被设置, 那么FreeMarker退回使用output_encoding的设置, 这个也会被默认设置, 所以它也是又软件设置的。如果output_encoding也没有被设置, 那么没有参数的内建函数url 将不会被执行, 而且它会引起运行时错误。当然, 有参数的url函数将会执行。
30.5. 用setting指令在模板中设置url_escaping_charset是可能的。至少在真实的MVC应用中, 这是一个不好的实践行为。output_encoding不能由setting指令来设置, 所以它应该是软件的工作。
31. url_path
31.1. 它和url内建函数相同, 只是它不转义斜杠(/)字符。这就是意味着用来转义使用了斜杠(不是反斜杠!)的路径(比如操作系统或一些内容仓库的路径), 转义之后它们可以插入到URL中。需要该转义的常用原因是文件夹名称或文件名称可能含有非US-ASCII字母("国家"标准符号)。
32. word_list
32.1. 包含字符串中所有单词的序列, 顺序为出现在字符串中的顺序。单词是不间断的字符序列, 包含了任意字符, 但是没有空白。
33. 通用标志
33.1. 很多字符串内建函数接受可选的字符串参数, 它们被称为"标志"。在这个字符串中, 每个字母影响内建函数一个特定方面的行为。比如, 字母i说明内建函数不应该区别相同字母的小写和大写变化。标志字符串中的字母顺序是不重要的。
33.2. 下面是字母(标志)的完整列表
33.2.1. i: 不区别相同字母的小写和大写变化。
33.2.2. f: 只是第一个。
33.2.3. r: 查找的子串是正则表达式。
33.2.4. m: 正则表达式的多行模式。在多行正则表达式^和$, 各自匹配行终止符或字符串结尾。
33.2.5. s: 开启正则表达式的dot-all模式。在dot-all模式下, 表达式.匹配任意字符串, 包含行终止符。默认情况下, 该表达式不匹配行终止符。
33.2.6. c: 允许正则表达式中的空白和注释。
33.3. 下表是支持使用这些通用标志的内建函数
34. 例子
34.1. 新建一个名为FMBuiltInsForStrings的动态Web工程, 同时添加相关jar包。
34.2. 编写FMFactory.java
package com.fm.util;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
public class FMFactory {
private final static FMFactory instance = new FMFactory();
private FMFactory() {}
public static FMFactory getInstance() {
return instance;
}
private Map<String, Configuration> map = new ConcurrentHashMap<String, Configuration>();
// 创建单个Configuration实例
public synchronized Configuration getCfg(Object servletContext, String path) {
if(null != map.get(path)) {
return map.get(path);
}
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
cfg.setServletContextForTemplateLoading(servletContext, path);
// 默认编码
cfg.setDefaultEncoding("utf-8");
// url转义编码
cfg.setURLEscapingCharset("utf-8");
// 输出编码
cfg.setOutputEncoding("utf-8");
// 设置可以进行布尔值格式化的值
cfg.setBooleanFormat("开,关");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
map.put(path, cfg);
return cfg;
}
}
34.3. 编写BuiltInsForStrings.java
package com.fm.action;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fm.util.FMFactory;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
public class BuiltInsForStrings extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Configuration cfg = FMFactory.getInstance().getCfg(req.getServletContext(), "/WEB-INF/templates");
Template template = cfg.getTemplate("builtinsforstrings.html");
Map<String, Object> root = new HashMap<String, Object>();
root.put("date", new Date(0));
root.put("time", new Time(0));
root.put("datetime", new Timestamp(0));
Writer out = new OutputStreamWriter(resp.getOutputStream());
try {
template.process(root, out);
} catch (TemplateException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
34.4. 修改web.xml
34.5. 在/WEB-INF/templates下编写builtinsforstrings.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>字符串内建函数</title>
</head>
<body>
<h2>字符串转为布尔值</h2>
<#assign bln = "true" />
<#if bln?boolean>
字符串"true"转布尔值成功。
</#if>
<#assign bln = "开" />
<#if bln?boolean>
字符串"开"转布尔值成功。
</#if>
<h2>字符串中的首单词的首字母大写</h2>
${"chinese china"?cap_first}
<h2>字符串中首单词的首字母小写</h2>
${"China And Chinese"?uncap_first}
<h2>字符串中所有单词的首字母大写</h2>
${"china chinese"?capitalize}
<h2>字符串的大写形式</h2>
${"China And Chinese"?upper_case}
<h2>字符串的小写形式</h2>
${"China And Chinese"?lower_case}
<h2>包含字符串</h2>
<#if "我们是中国人"?contains("中国人")>
我们是中国人包含<b>中国人</b>。
</#if>
<h2>字符串转换成日期值, 时间或日期-时间值</h2>
${"1970-1-1"?date}
${"8:00:00"?time}
${"1970-1-1 8:00:00"?datetime}
<h2>字符串是否以指定的子字符串开头</h2>
<#if "我们是中国人"?starts_with("我们")>
我们是中国人以<b>我们</b>开头。
</#if>
<h2>字符串是否以指定的子字符串结尾</h2>
<#if "我们是中国人"?ends_with("中国人")>
我们是中国人以<b>中国人</b>结尾。
</#if>
<h2>以指定字符串开头</h2>
${"WEB-INF/templates"?ensure_starts_with("/")}<br />
${"localhost:8080/"?ensure_starts_with("https?://", "http://")}<br />
<#-- r查找的子串是 正则表达式。i比较是大小写不敏感的。-->
${"HTTPS://localhost:8080/"?ensure_starts_with("https?://", "http://", "ri")}
<h2>以指定字符串结尾</h2>
${"WEB-INF/templates"?ensure_ends_with("/")}
<h2>返回第一次字符串中出现子串时的索引位置</h2>
"我们是中国人我们是中国人", 第一个"们"的位置: ${"我们是中国人我们是中国人"?index_of("们")}<br />
"我们是中国人我们是中国人", 从第-3的位置起, 第一个"们"的位置: ${"我们是中国人我们是中国人"?index_of("们", -3)}<br />
"我们是中国人我们是中国人", 从第20的位置起, 第一个"们"的位置: ${"我们是中国人我们是中国人"?index_of("们", 20)}<br />
"我们是中国人我们是中国人", 从第7的位置起, 第一个"们"的位置: ${"我们是中国人我们是中国人"?index_of("们", 7)}
<h2>返回最后一次(最右边)字符串中出现子串时的索引位置</h2>
"abcabc", 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab")}<br />
"abcabc", 从-2的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", -2)}<br />
"abcabc", 从-1的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", -1)}<br />
"abcabc", 从0的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", 0)}<br />
"abcabc", 从1的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", 1)}<br />
"abcabc", 从2的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", 2)}<br />
"abcabc", 从3的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", 3)}<br />
"abcabc", 从4的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", 4)}<br />
"abcabc", 从5的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", 5)}<br />
"abcabc", 从6的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", 6)}<br />
"abcabc", 从7的位置算, 最后一次"ab"出现的位置: ${"abcabc"?last_index_of("ab", 7)}
<h2>字符串按照html标记输出</h2>
${"<b>\"true\" & 'false'</b>"?html}
<h2>移除字符串中的一部分内容</h2>
<#-- \s匹配空格 -->
${"abcdef"?keep_before("de")}<br />
${"foo : bar"?keep_before(r"\s*:\s*", "r")}<br />
${"abcdefgh"?keep_after("de")}<br />
${"foo : bar"?keep_after(r"\s*:\s*", "r")}<br />
${"foo.bar.txt"?keep_before_last(".")}<br />
${"foo.bar.txt"?keep_after_last(".")}
<h2>开始位置插入足量空白</h2>
<pre>[${"abc"?left_pad(6)}]</pre>
<pre>[${"abc"?left_pad(3)}]</pre>
[${"abc"?left_pad(6, "-")}]<br />
[${"abc"?left_pad(6, "-*&")}]
<h2>结束位置插入足量空白</h2>
<pre>[${"abc"?right_pad(6)}]</pre>
<pre>[${"abc"?right_pad(3)}]</pre>
[${"abc"?right_pad(6, "-")}]<br />
[${"abc"?right_pad(6, "-*&")}]
<h2>字符串中字符的数量</h2>
"我们都是中国人。"字符数量: ${"我们都是中国人。"?length}
<h2>字符串转化为数字格式</h2>
"123"转化为数字格式: ${"123"?number}<br />
"1.23E6"转化为数字格式: ${"1.23E6"?number}<br />
"1.5e-8"转化为数字格式: ${"1.5e-8"?number}
<h2>替换字符串</h2>
${"我们都是中国人。我们都是中国人。"?replace("们都", "")}<br />
${"我们都是中国人。"?replace("", "|")}<br />
${"aaaaa"?replace("aaa", "X")}<br />
${"aaaaaAAAAA"?replace("aaa", "X", "i")}<br />
<h2>从字符串的开头移除参数中的子串</h2>
${"abcdef"?remove_beginning("abc")}<br />
${"defabc"?remove_beginning("abc")}
<h2>从字符串的结尾移除参数中的子串</h2>
${"defabc"?remove_ending("def")}<br />
${"abcdef"?remove_ending("def")}
<h2>分割字符串</h2>
<#list "some,,test,text,"?split(",") as x>
"${x}"<br />
</#list>
<h2>去掉字符串首尾的空格</h2>
[${" 你好哇 "?trim}]
<h2>在URL之后的字符串进行转义</h2>
${"a/b c"?url("ISO-8859-1")}<br />
${"<>'"?url("UTF-8")}<br />
<#-- 设置url转义编码, 最好不要在这里设置, 在程序里设置。 -->
<#setting url_escaping_charset="UTF-8">
${"a/b c"?url}<br />
${"<>'"?url}
<h2>url_path不转义斜杠</h2>
${"/<>/'/"?url_path("UTF-8")}
<h2>包含字符串中所有单词的序列</h2>
<#assign words = " a bcd, . 1-2-3"?word_list />
<#list words as word>${word}<#sep><br /></#sep></#list>
</body>
</html>
34.6. 运行项目