Java基础(8)-----字符串

思维导图

一.String类

    String类是Java中用来表示字符串的类,有一些有趣的特性.

  •     1.String对象是不可变的,也就是说,对String的变动其实返回的都是一个新的String对象,比如string+string.
//测试String的不变特性
    public void test1(){
        String s1 = "ceshi";
        String s2 = s1.toUpperCase();
        //有这个输出可以看到,s1没有改变
        System.out.println(s1);

        System.out.println(s2);
    }
  •  2.Java中的+和+=对String类做了重载,表示连接两个字符串
  • 3.当对象重写自己的toString方法时,如果用string+this将会导致无意识的递归.
//测试无意识的递归
    public void test3(){
        class TestA{
            @Override
            public String toString() {
                //会将this自动转化为调用其toString方法,从而导致递归
                return "TestA{}" + this;
            }
        }
    }

字符串还有一系列功能相当强大的方法,这里不一一列举,后文中会提到一些关于正则表达式的方法.

二.格式化输出

格式化输出的语法:

  •     %[-][index$][flags][width][.precision]conversion
  1.     默认为右对齐,有了'-'后,变为左对齐
  2.     index$可以指定输入数据的位置,比如:String.format("我叫%2$s,她叫%1$s", "小明","小方");会输出我叫小方,她叫小明
  3.     flags则是一些特殊标记,比如:','表示金额用千分位分开,0表示结果用0填充
  4.     width表示数据的最小尺寸
  5.     .precision则表示精度,也可以表示最大尺寸,只用于String和浮点数
  6.     conversion表示占位符,表示后方输入数据的类型,比如:s-字符串,d-整数,c-unicode字符,f-浮点数,e-科学计数法,x-16进制整       数,h-散列码,b-布尔类型,可以进行数据类型的转换

怎样调用格式化输出的方法

    Java中为格式化输出准备了一个工具类,叫Formtter,用于进行格式化输出,创建时需要指定输出到哪

//用Formatter类测试格式化输出
    public void testFormat(){
        //创建Formatter要指定输出到哪
        Formatter formatter = new Formatter(System.out);
        formatter.format("test1:%d\n",1);
        //'-'表示左排列,默认是右排列,所以输出中hello和lalala都不会和----挨在一起
        formatter.format("test2:%-10.20s----%10.20s","hello","lalala");
    }

    除此之外,Stirng.format()方法,和PrintStream的printf()/format()方法都可以进行格式化输出,但是这些方法本质上都是调用Formatte实现的.

    PS:System.out是标准IO,是一个PrintStream,所以可以调用printf()/format()方法.

三.正则表达式

    正则表达式是一个可以对文本进行高效处理的工具,在Java中,他比较特殊,即用\\代替了其他语言中的\表示正则表达式的反斜线,普通的字符'\''则用'\\\\'来表示

3.1正则表达式的语法:

字符
x 字符 x 
\\\\ 反斜线字符
\0n 带有八进制值 0 的字符 n (0 <= n <= 7) 
\0nn 带有八进制值 0 的字符 nn (0 <= n <= 7) 
\0mnn 带有八进制值 0 的字符 mnn(0 <= m <= 3、0 <= n <= 7) 
\xhh 带有十六进制值 0x 的字符 hh 
\uhhhh 带有十六进制值 0x 的字符 hhhh 
\t 制表符 ('\u0009') 
\n 新行(换行)符 ('\u000A') 
\r 回车符 ('\u000D') 
\f 换页符 ('\u000C') 
\a 报警 (bell) 符 ('\u0007') 
\e 转义符 ('\u001B')
\cx  对应于 x 的控制符 
字符类
[abc]   a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定) 
[a-zA-Z]   a 到 z 或 A 到 Z,两头的字母包括在内(范围
[a-d[m-p]]  a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]]  d、e 或 f(交集)
. 任何字符(与行结束符可能匹配也可能不匹配) 
\d 数字:[0-9] 
\D 非数字: [^0-9] 
\s 空白字符:[ \t\n\x0B\f\r] 
\S

非空白字符:[^\s] 

\w 单词字符:[a-zA-Z_0-9] 
\W 非单词字符:[^\w] 
逻辑操作符
XY X 后跟 Y
X|Y X 或 Y 
(X)  X,作为捕获组 

这里需要对组的概念进行说明,正则表达式匹配的的字符串整体是group(0),而如果正则表达式中有用()分组,那么组的排列顺序从左到右计算,用group(i)获得.看下例:

//测试组的分配和获得
    public void testGroup(){
        String testString = "hello,my regex";

        //在这个正则表达式中,group(0)是整体上被匹配的,也就是testString,
        //group(1)是','group(2)是mygroup(3)则是my后的所有字符
        Pattern p = Pattern.compile("h\\w+(,)(my)(.+)");
        Matcher m = p.matcher(testString);

        while(m.find()){
            System.out.println(m.group(0));
            System.out.println(m.group(1));
            System.out.println(m.group(2));
            System.out.println(m.group(3));
        }
    }
    /*out
    hello,my regex
            ,
    my
     regex
     */
边界匹配符
^ 行的开头 
$ 行的结尾
\b 单词边界
\B 非单词边界 
\A 输入的开头 
\Z 输入的结尾,仅用于最后的结束符(如果有的话) 
\z 输入的结尾 
\G 上一个匹配的结尾 

量词分为贪婪型,勉强型,占有型,勉强型会在贪婪型的基础上加一个?,占有型则是加一个+.

贪婪型量词
X? X,一次或一次也没有 
X* X,零次或多次 
X+ X,一次或多次 
X{n} X,恰好 n 次 
X{n,}  至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次

贪婪型,勉强型,占有型量词的区别举例:

  • 贪婪型 :java正则表达式默认匹配是贪婪型,就是原始表达式(x?),会 为所有可能的模式发现尽可能多的匹配。
  • Matcher m = Pattern.compile("x+").matcher("xxxxxx");
    
    if(m.find()){
        System.out.println(m.group());
    }
                
    /*Oputput:
    xxxxxx
    */
  • 勉强型 :原始表达式后边加个?号(x+?),会匹配满足模式所需的最少字符数。
  • Matcher m = Pattern.compile("x+?").matcher("xxxxxx");
    
    if(m.find()){
        System.out.println(m.group());
    }
                
    
    /*Output:
    x
    */  
  • 占有型 :原始表达式后边加个+号(x++), 当正则表达式被应用于字符串时,他会产生相当多的状态,以便匹配失败时可以回                    溯。而“占有型”量词不会保存这些中间状态,因此他们可以防止回溯。他们常常用于防止正则表达式失败,因此可                    以使用正则表达式起来更加有效。
  • //测试占有型量词
        public void test99(){
            //  \\d+都匹配到了3 6 0 3,遇到空格就无法匹配,导致后面的\\d就无法匹配,他就进行回溯,最终让后一个\\d匹配3 6 0 3
            Matcher m = Pattern.compile("\\d+\\d").matcher("123 456 78910 111213");
            
            while(m.find()){
                System.out.println(m.group());
            }
    
            System.out.println("----------------------------------------");
    
            //  但是占有型则不回溯,一个也匹配不成
            Matcher m2 = Pattern.compile("\\d++\\d").matcher("123 456 78910 111213");
            
            while(m2.find())
                System.out.println(m2.group());
        }
    /*Output:
    123
    456
    78910
    111213
    ----------------------------------------
    */
    标记模式

    CANON_EQ

    启用规范等价。

    指定此标志后,当且仅当其完整规范分解匹配时,两个字符才可视为匹配。例如,当指定此标志时,表达式 "a\u030A" 将与字符串 "\u00E5" 匹配。默认情况下,匹配不考虑采用规范等价。

    不存在可以启用规范等价的嵌入式标志字符。

    CASE_INSENSITIVE(?i)

    启用不区分大小写的匹配。默认情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集中的字符。可以通过指定 UNICODE_CASE 标志连同此标志来启用 Unicode 感知的、不区分大小写的匹配

    COMMENTS (?x)

    模式中允许空白和注释。

    此模式将忽略空白和在结束行之前以 # 开头的嵌入式注释。

    通过嵌入式标志表达式  (?x) 也可以启用注释模式。

    DOTALL(?s)

    启用 dotall 模式。

    在 dotall 模式中,表达式 . 可以匹配任何字符,包括行结束符。默认情况下,此表达式不匹配行结束符。

    通过嵌入式标志表达式 (?s) 也可以启用 dotall 模式(s 是 "single-line" 模式的助记符,在 Perl 中也使用它)。

    LITERAL

    启用模式的字面值解析。

    指定此标志后,指定模式的输入字符串就会作为字面值字符序列来对待。输入序列中的元字符或转义序列不具有任何特殊意义。

    标志 CASE_INSENSITIVE 和 UNICODE_CASE 在与此标志一起使用时将对匹配产生影响。其他标志都变得多余了。

    MULTILINE(?m)

    启用多行模式。

    在多行模式中,表达式 ^ 和 $ 仅分别在行结束符前后匹配,或者在输入序列的结尾处匹配。默认情况下,这些表达式仅在整个输入序列的开头和结尾处匹配。

    通过嵌入式标志表达式 (?m) 也可以启用多行模式。

    UNICODE_CASE (?u)

    启用 Unicode 感知的大小写折叠。

    指定此标志后,由 CASE_INSENSITIVE 标志启用时,不区分大小写的匹配将以符合 Unicode Standard 的方式完成。默认情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集中的字符。

    通过嵌入式标志表达式 (?u) 也可以启用 Unicode 感知的大小写折叠。

    使用举例:

//这两个是一样的
Pattern p = Pattern.compile("h\\w+(,)(my)(.+)",Pattern.MULTILINE | 
                                               Pattern.CASE_INSENSITIVE);
Pattern p = Pattern.compile("(?m)(?i)h\\w+(,)(my)(.+)");

3.2正则表达式的调用

在写语法的时候已经简单的使用了matcher的一部分方法,他还有更多的实用方法.

//测试Matcher的各种方法
    public void testMatcher(){
        String s = "nigoule xiaohao\n"+
                   "nihao wobugou";

        //匹配以n开头,内部含有元音字母的单词
        Pattern pattern = Pattern.compile("\\bn(\\w*)[aeiou](\\w*)\\b");//贪婪型

        Matcher matcher = pattern.matcher(s);

        //使用find()以发现匹配的部分
        while(matcher.find()){
            //测试group()方法和start(),end()
            System.out.println("group:" + matcher.group());
            System.out.println("start():" + matcher.start());
            System.out.println("start():" + matcher.end());
            //group() 等同于 group(0)
            System.out.println("group(0):" + matcher.group(0));
            System.out.println("group(1):" + matcher.group(1));
            System.out.println("start():" + matcher.start(1));
            System.out.println("start():" + matcher.end(1));

            //贪婪模式会匹配到最后一个元音字母,所以group(2)是空的
            System.out.println("group(2):" + matcher.group(2));

            //匹配到字符串后的修改使用appendReplacement()
            //必须有StringBuffer,以储存修改后的字符串
            StringBuffer sb = new StringBuffer();
            matcher.appendReplacement(sb,"XXXXX");
            //将匹配后的字符串输出
            //在while循环内只会将本次匹配的字符串替换,如果在while调用此方法,则会将所有的匹配都替换掉
            System.out.println(matcher.appendTail(sb));

            //使用lookingAt()要注意匹配会重新重头开始,以本次的String举例,调用会导致死循环,因为代码的最后又重头匹配了,而且nigoule这个字符串会由于被lookingAt匹配到从而导致输出中不会有它.
            System.out.println(matcher.lookingAt());//若不想死循环,请注释掉这条代码.

            //matcher()会导致lookingAt()一样的问题
            System.out.println(matcher.matches());//若不想死循环,请注释掉这条代码.
            System.out.println("--------------------------------------------");

            //测试reset()方法,重置匹配字符串,CharSequences是字符串序列的抽象接口
            //matcher.reset("nihao,世界helloworld");
            

        }
    }

四.Scanner工具类

    Scanner类是util包中的一个工具类,主要用途就是扫描文本以获得数据,它大大减少了扫描输入的工作量.

    在API文档中,可以看到,Scanner中有获得各种各样基本数据类型的方法,同时,他也可以获得匹配正则表达式的文本.

猜你喜欢

转载自blog.csdn.net/zh328271057/article/details/81711590