Java 编程思想笔记:Learn 9

13 字符串

13.1 不可以变的 String

string 对象是不可变的。String 类中每一个看起来会修改 String 值的方法,其实都是创造了一个全新的 String 对象。其实这正是我们想要的,对一个方法而言,参数只是提供信息,而不是想让方法改变自己的。

13.2 重载 “+” 与 StringBuilder

String 对象是不可变的,你可以给 String 对象加任意多的别名。因为 String对象具有只读特性,所以任何指向它的引用都不可能改变它的值,因此,也就不会对其他应用产生什么影响。

不可变性会带来效率问题。为 String 对象重载的 “+” 操作符就是一个例子。重载的意思是,一个操作符在应用于特定的类时,被赋予了特殊的意义(用于 String 的 + 和 += 是Java中仅有的两个重载操作符,而Java并不允许程序员重载任何操作符。)

操作符 “+” 可以用来连接 String:

String s = "a" + "b" + "c"

这样简单的语句,编译器会做出优化。但是如果在方法中使用循环,应该使用StringBuilder.

public class UsingStringBuilder {

    public static Random rand = new Random(47);

    public String toString(){
        StringBuilder result = new StringBuilder("[");
        for(int i = 0;i < 25; i++){
            result.append(rand.nextInt(100));
            result.append(". ");
        }
        result.delete(result.length() -2, result.length());
        result.append("]");
        return result.toString();
    }

    public static void main(String[] args){
        UsingStringBuilder usb = new UsingStringBuilder();
        System.out.println(usb);
    }
}

StringBuilder 提供了非常丰富的方法,包括 insert() / replace() / subString() / reverse(), 但是最常用的还是 append() 和 toString(), 还有 delete() 方法。

StringBuilder 是 java5 引进的,在此之前使用的是 StringBuffer, StringBuffer 是线程安全的,因此开销会有些大。

13.3 无意识的递归

java 中的每个类从根本上都是继承自 Object,标准容器也是如此,因此容器类都有 toString() 方法。并且覆盖了该方法,使得它生成的 string 结果能表达容器自身,以及容器所包含的对象。

13.4 String 的操作

方法 参数,重载版本 应用
length String 中字符串的个数
charAt Int 索引 取得String上该索引位置上的 char
getChars() getBytes() 要复制部分的起点和终点的索引,复制目标数组,目标数组的起始索引 复制 char 或 byte 到一个目标数组的中
toCharArray() 生成一个char[],包含String 中所有的字符
equals, equalsIgnoreCase() 与之进行比较的String 比较两个String 的内容是否相同
compareTo 与之比较的String 按词典的位置比较String的内容,比较结果为负数、零或正数。大小写不等价
contains 要搜索的CharSequence 返回bool
contentEquals 与之比较的charSequence或StringBuffer 如果该String与参数完全一直,则返回true
equalsIgnoreCase 与之进行比较的String 忽略大小写,比较内容
regionMatcher 该String的索引偏移量,另一个String及其索引偏移量 返回boolean结果
startsWith 可能的起始String 返回 boolean结果
endsWith 该String可能的后缀String 返回 boolean 结果
indexOf, lastIndexOf 重载版本包括:char, char 与 起始索引,String,String与起始索引 如果不包含此参数则返回-1,lastIndexOf 是从后向前索引
substring() 起始索引 截取字符串
concat join字符串
replace 替换字符串
toLowerCase / toUpperCase 字符串大小写转化
trim 将string 两端的空白符删除后,返回一个新的字符串
valueOf 返回一个表示参数内容的String
intern 为每个唯一的字符序列生成一个String引用

13.6 正则表达式

反斜线:
java 中 \ 表示正则表达式中的反斜线。如python 中的 \d 要写为 \d
\\ 表示转义,表示反斜线本身。

将数组打印出来
一般来说,Java 原生的 API 一般转化为装箱类型,然后用包装类型的类型 toString 方法来输出

// 数组转化为 Array 输出
String[] s = {"a", "b", "c"}
System.out.println(Arrays.toString(s  ))

String 中使用正则表达式的API,String.split() / String.replace()

13.6.2 创建正则表达式

正则表达式字符类

- 字符类
. 任意字符
[abc] 包含a b c的任何字符和a/b/c相同
[^abc] 除a b c之外的任何字符
[a-zA-Z] 从A-Z 或 从a-z 的任何字符
[abc[hij]] a / b / c / h/i/j的作用相同,也就是匹配其中一个字符。python js 不支持此语法
a-z&&[hij] 任何hij中任意1个,python js 不支持此表达
\s 空白符,空格、tab / 换行 、换页、回车
\S [^\s] 非空白符
\d 数字[0-9]
\D 非数字,[^0-9]
\w 词字符(word),[a-zA-Z0-9]
\W 非词字符,[^\w]

逻辑操作符号

逻辑操作符
xy y跟在x后面
x y x或y
(x) 捕获组,可以在表达式中用\i引用第i个捕获组
边界匹配符号
^ 一行的起始
$ 一行的结束
\b 词的边界
\B 非词的边界
\G 前一个匹配的结束

边界匹配符号

边界匹配符号
^ 一行的起始
$ 一行的结束
\b 词的边界
\B 非词的边界
\G 前一个匹配的结束

13.6.3 量词

量词描述了一个模式吸收输入文本的方式。

  • 贪婪型:量词总是贪婪的,除非有其他选项被设置。
  • 勉强型:这个量词匹配满足模式所需的最少字符数
  • 占有型:仅在Java 中可用。用于防止表达式失控
贪婪型 勉强型 占有型 如何匹配
x? x?? x?+ 一个或零个x
x* x*? x*+ 零个或多个x
x+ x+? x++ 1个或多个x
x{n} x{n}? x{n}+ 恰好n个x
x{n,} x{n,}? x{n}+ 至少n个x
x{n,m} x{n,m}? x{n,m}+ n,m个x

表示式x通常必须要用圆括号括起来,以便它能按照我们期望的方式工作,比如 (abc)+

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

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

这些类都实现了该接口.多数正则表达式接受 CharSequence 作为参数

13.6.4 Pattern 和 Matcher

导入 regex 包,使用 static Pattern.compile 方法编译你的正则表达式. 这个方法会生成一个 Pattern 对象.这点和 Python 一样.

接下来,可以把想检索的字符串给 Pattern 的 matcher 方法.matcher 方法会生成一个 Matcher 对象,它有很多功能可用.

public class TestRegularExpression {
    static String s = "abcabcabcdefabc";
    public static void main(String[] args){
        print("abc+");
        print("(abc)+");
        print("(abc){2,}");
    }

    private static void print(String p){
        System.out.println(p);
        Pattern pattern = Pattern.compile(p);
        Matcher matcher = pattern.matcher(s);
        while (matcher.find()){
            System.out.println("Match \""+ matcher.group() +"\" at positions " + matcher.start() + "-" + (matcher.end()-1));
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>");
    }
}
组(groups)

组是用括号划分的正则表达式,可以依据组的编号来引用某个组.组号为0 表示整个表达式,组号为1表示第一对括号括起来的正则表达式,以此类推

A(B(C))D

这个表达式中有三个组, 组0 是 ABCD, 组1 是 BC, 组2 是C

Matcher 对象提供了一系列方法,用以获取和组相关的信息:

  • public int groupCount ,返回该匹配器模式中的分组数目,第0组不包括在内
  • public String group 返回前一次匹配操作(例如find)的第0组
  • public String group(i) 返回前一次匹配操作的第i组,如果匹配成功,但是指定的组没有匹配输入的字符串的任何部分,返回null
  • public int start(int group) 返回前一次匹配操作中寻找到的组的起始索引
  • public int end(int group) 返回前一次匹配操作中寻找到组的最后一个字符索引加一的值
public class Groups {
    static public final String POEM = "I came as tomorrow\n"+
            "\n"+
            "Swaddled in innocence\n"+
            "\n"+
            "To your warm womb\n"+
            "\n"+
            "Mother……\n"+
            "\n"+
            "Without your choice\n"+
            "\n"+
            "Or mine\n"+
            "\n"+
            "Destined to up date\n"+
            "\n"+
            "With time";

    public static void main(String[] args){
        Matcher m = Pattern.compile("(?m)(\\S+)\\s+(\\S+)$").matcher(POEM);
        while (m.find()){
            for(int j = 0; j < m.groupCount();j++){
                System.out.print("[" + m.group(j) + "]");
            }
            System.out.println();
        }
    }
}
send() 与 end()

在匹配操作成功后,start() 返回先前匹配的起始位置的索引,而 end() 返回所匹配的最后字符的索引加一的值。

Pattern 标记
编译标记 效果
Pattern.CANON_EQ 两个字符当且仅当它们的完全规范分解相匹配时,就确认它们是匹配的。例如,如果我们指定这个标记,表达式 a\u030A 就会匹配字符串。
Pattern.CASE_INSENSITIVE(?!) 忽略大小写
Pattern.COMMENTS(?x) 空格符将被忽略,# 从开始到行尾的注释也忽略掉
Pattern.DOTALL(?s) 默认情况下,“.”表达式不匹配行终结符;此模式下,. 匹配一切符号
Pattern.MULTILINE(?m) 在多行模式下,表达式 ^ 和 $ 分别匹配一行的开始和结束
Pattern.UNICODE_CASE(?u) 看不懂
public class ReFlags {

    public static void main(String[] args){
        //多行,大小写不敏感
        Pattern p = Pattern.compile("^java", Pattern.CASE_INSENSITIVE| Pattern.MULTILINE);
        Matcher m = p.matcher("java has regex\njava has regex\n" +
                "java has regex java has regex\n" +
                "JAVA has regex");
        while (m.find()){
            System.out.println(m.find());
            System.out.println(m.group());
        }
    }
}

13.6.5 split

split() 方法将输入字符串断开成字符串对象数组,断开边界由下列正则表达式确定:

String[] split(CharSequence input)
String[] split(CharSequence input, int limit)

case

public class SplitDemo {
    public static void main(String[] args){
        String input = "This is!This is!This is!This is!This is!This is!";
        System.out.println(Arrays.toString(Pattern.compile("!").split(input)));
        System.out.println(Arrays.toString(Pattern.compile("!").split(input, 3)));
    }
}

1.3.6.6 替换操作

  • replaceFirst(String replacement)以参数字符串 replacement 替换第一个匹配成功的部分
  • replaceAll(String replacement) 以参数字符串 replacement 替换掉所有匹配成功的部分
  • appendReplacement(StringBuffer sbuf, String replacement), 渐进式替换。它允许你调用其他方法来生成或处理 replacement,以编程的方式分割成组,从而有更强大的替换能力。
  • appendTail(StringBuffer sbuf), 在执行一次或多次 appendReplacement() 之后,调用此方法可以将输入字符串余下的部分复制到 sbuf 中。

13.6.7 reset()

reset , 去匹配新的字符串

public class Resetting {

    public static void main(String[] args) throws Exception{
        Matcher m = Pattern.compile("[frb][aiu][gx]").matcher("fix the rug with bags");
        while (m.find()){
            System.out.println(m.group() + " ");
        }
        m.reset("fag fag fag fag");
        while (m.find()){
            System.out.println(m.group() + " ");
        }
    }
}

13.7 扫描输入

你可能感兴趣的:(Java 编程思想笔记:Learn 9)