Java 之路 (十三) -- 字符串(String、正则表达式)

个人吐槽:对于这章,第一遍读和重读之后果然看法不同:第一次读时觉得,文章用了大篇幅来介绍 API,这样我可以比较容易理解这个方法是干嘛的,以此理解这个类是干嘛的;但是重读之后觉得,文章居然用了这么大篇幅在讲 API(尤其是正则表达式),如果我需要知道具体某个方法怎么用,直接去官方文档查多好啊,这种情况下,我更希望得到某个方法可能出现的坑,而不只是方法的介绍(毕竟,这往往需要踩过坑之后总结的,hhh)。


1. String 基础

1.1 String 不可变

  • String 对象是不可变的。(只读性)

  • String 类中每一个修改 String 值的方法,实际上都是创建了一个新的 String 对象,原有的 String 对象丝毫未动。

1.2 String 的连接问题

"+" 和 "+=" 是经过 Java 重载之后的两个操作符,可用来对 String 拼接。

同时 Java 不允许程序员重载任何操作符。

String + String 与 StringBuilder.append() 对比

  1. 直接相加字符串(不含 String 引用,形如"hello"+"world"):效率最高,简单来说,其全部为常量,编译期就会优化为 "helloworld",编译期值就已经确定了。

  2. 间接相加字符串(含 String 引用,如 str + "world"):效率最差,简单来说会取出原有 String 与 "world" 进行拼接,然后将结果存入另一个新的 String 对象中,虽然编译器进行了优化,通过 StringBuilder.append() 来实现拼接,但是当出现循环时,会出现 StringBuilder 对象的反复生成。

  3. StringBuilder.append():介于上述二者之间。

    综上:

    1. 如果是固定字符串如“xxx”,那么直接用 "+" 即可。
    2. 如果间接相加字符串,但是次数很少,则 StringBuilder 和 "+" 都可,差别不很大
    3. 如果简介相加字符串,且多次循环,那么强烈建议 StringBuilder

1.3 无意识的递归

当想要打印出某对象的内存地址时,应该调用 Object.toString() / super.toString() 方法,而非 this 上的 toString()。

换句话说,就是当前对象重写的 toString() 方法中,应该调用 super.toString() 而不是使用 this。

在当前对象的 toString() 方法中,如果调用 this,会发生自动类型转换,将对象类型转换为 String 类型。那么怎么转换呢?正式通过调用 this (当前对象)上的 toString() 方法。

em...完美的形成一个递归,自己调用自己,且没有终止条件。

1.4 String API

这里有想知道的一切 API

https://docs.oracle.com/javase/9/docs/api/java/lang/String.html

1.5 格式化输出

如下几种方式:

  1. System.out.println() 强行凑成指定形式,不推荐

  2. System.out.format():针对于 PrintStream 和 PrintWriter,其中包括 System.out 对象

  3. Formatter 类:可以看作是一个翻译器,将格式化字符串与数据翻译成需要的结果。

    1. 格式化说明符:%[argument_index$][flags][width][.precision]conversion

      width 指定域的最小尺寸

      precision 指定最大尺寸,适用于 String 和浮点数,无法用于整数

    2. 常用的类型转换

      类型转换字符 意义 类型转换字符 意义
      d 十进制整数型 e 科学计数浮点数
      c Unicode 字符 x 十六进制整数
      b Boolean 值 h 十六进制散列码
      s String % 字符 "%"
      f 十进制浮点数

      更多详见 https://docs.oracle.com/javase/9/docs/api/java/util/Formatter.html

  4. String.format()

    1. 实际上就是内部封装了 Formatter 对象,然后将传入的参数传递给 Formatter,由它来进行具体的处理。

2. 正则表达式

2.1 通用正则表达式

就不整理出来了,放上一篇教程

http://www.runoob.com/regexp/regexp-tutorial.html

https://docs.oracle.com/javase/9/docs/api/java/util/regex/Pattern.html

补充一点:

  • 在 java 中,"\\" 表示一个有效的 "\",因此如果需要表示正则表达式里的一个数字 "\d",需要写成 "\\d"

2.2 Pattern & Matcher

接口 CharSequence 从 CharBuffer、String、String Buffer、StringBuilder 类之中抽象出了字符序列的一般化定义:

interface CharSequence {
    charAt(int i);
    length();
    subSequence(int start,int end);
    toString();
}

本小节简单介绍 Java 中如何处理正则表达式:

2.2.1 基础使用:

step 1:Pattern.compile(regex) 编译 String 类型的 regex,并产生 Pattern 对象

step 2:Pattern.matcher(待检索的字符串)生成一个 Matcher 对象

直接上例子,通过例子说明:

import java.util.regex;

public class TestRegularExpression {
    public static void main(String[] args) {
        String[] array = {"aabbcc", "aab", "aab+", "(b+)"};
        
        for (String arg : array) {
            System.out.println();
            print("Regular expression: \"" + arg + "\"");
            Pattern p = Pattern.compile(arg); // step1: Pattern 表示编译后的匹配模型Pattern.(编译后的正则表达式)
            Matcher m = p.matcher("aabbcc"); // step2: 模型实例 检索 待匹配字符串并 生成一个匹配对象Matcher, Matcher有很多方法
            while (m.find()) {
                print("Match \"" + m.group() // 待匹配的字符串
                                 + "\" at positions " 
                                 + m.start() // 字符串匹配regex的起始位置
                                 + "-" + (m.end() - 1)); // 字符串匹配regex的终点位置
            }
        }
    }

Pattern 对象表示编译后的正则表达式,重点在于 Matcher 对象,它提供了一系列方法来进行正则的匹配,下面简单介绍:

详见 https://docs.oracle.com/javase/9/docs/api/overview-summary.html

  1. Matcher.find():用来在 CharSequence 查找多个匹配

  2. Matcher.group():用来获取与组相关的信息

    组是用括号划分的正则表达式,可以用组的编号来引用某个组,组0表示整个表达式,组1表示被第一队括号括起来的组。。。

    A(B(C))D : 组0是 ABCD,组1是B,组2是C

  3. Matcher.start() & end():返回先前匹配的起始和截止位置的索引。

  4. Pattern 标记:重载方法,Pattern.compile(String regex, int flags) 接受一个 flags 参数,来调整匹配的行为。具体看官方文档:

    https://docs.oracle.com/javase/9/docs/api/java/util/regex/Pattern.html

  5. split():将输入字符串断开成字符串对象数组。

  6. 替换 replacexxx():替换文本

  7. reset():将现有的 Matcher 对象应用于一个新的字符序列。

3. 扫描输入

Scanner 是 Java SE5 中添加的特性,主要就是减轻扫描输入的工作负担,最实用的是获取控制台输入,其他比如从文件读取内容的,emmm...感觉有些鸡肋。

举个使用的简单例子:

public class SimpleScanner { 
        public static void main(String[] args) { 
                Scanner s = new Scanner(System.in); 
                System.out.println("请输入字符串:"); 
                while (true) { 
                        String line = s.nextLine(); 
                        if (line.equals("exit")) break; 
                        System.out.println(">>>" + line); 
                } 
        } 
}

/*
请输入字符串:
whdalive
>>>whdalive
exit

Process finished with exit code 0
*/

Scanner 使用很方便,这是因为它的构造器可以接受任何类型的输入对象,有了 Scanner 之后,所有输入、分词以及翻译的操作都隐藏在不同类型的 next 方法中。

3.1 Scanner 定界符

默认情况下,Scanner 根据空白字符对输入进行分词,但是我们可以用正则表达式指定自己所需的定界符。

public static void main(String[] args) throws FileNotFoundException { 
    Scanner s = new Scanner("12,42,78,99,42"); 
    s.useDelimiter(","); 
    while (s.hasNext()) { 
        System.out.println(s.next()); 
    } 
}
/*输出
12
42
78
99
42
*/

我们通过 useDelimiter() 方法来指定定界符,显然上述代码中我们使用的是逗号。

3.2 正则表达式扫描

Scanner 的 next 方法中,有一个重载方法可以接收 String 的正则表达式,此时它会找到下一个匹配该模式的输入部分,然后调用 match() 方法就可以获得匹配的结果。工作方式和正则表达式匹配是类似的。

但是需要注意一点:它仅仅针对下一个输入分词进行匹配,如果正则表达式中有定界符,那么永远不可能匹配成功。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259

推荐阅读更多精彩内容