Java编程思想 第13章字符串 学习笔记

不可变String

  • String实际上是不可变的,每一个看起来会修改String的方法,实际上都是创建一个全新的String对象。
      public static void main(String[] args) {
          
          
        String q = "abc";
        String qq = q.toUpperCase();
        System.out.println(q);  // abc
        System.out.println(qq);  // ABC
    }
    
  • 当传入toUpperCase()的时候,实际上传递的是引用的一个拷贝。
  • 对于一个方法而言,参数是为该方法提供信息的,而不是想让该方法改变自己的。
  • String对象是不可变的,可以给String对象添加任意多的别名。
    String q = "abc";
    String qqq = q;   // 别名
    System.out.println(q == qqq);  // 地址相同,true
    

重载“+”与StringBuilder

  • 通过”+“可以连接两个String
    底层原理:虽然源代码中没有使用StringBuilder类,但编译器却自作主张地使用了它,因为它更高效。编译器创建了一个StringBuilder对象,用以构造最终的String,并在每个字符串调用一次StringBuilderappend()方法,最后调用toString()生成结果。
    String s = "abc" + "mango" + "def" + 47;  // abcmangodef47
    
  • StringStringBuilder比较
    第一种方法,每经过一次循环,就会创建一个新的StringBuilder对象,然后在append();第二种方法,只创建一个StringBuilder对象,然后直接在StringBuilderappend
    显式地创建StringBuilder还允许你预先为其指定大小。如果你已经知道最终的字符串大概有多长,那预先为其指定大小
      public static void main(String[] args) {
          
          
        String res = "";
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
          
          
          res += i;
        }
        System.out.println(System.currentTimeMillis() - start);  // 65
    
        StringBuilder result = new StringBuilder();
        start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
          
          
          result.append(i);
        }
        System.out.println(System.currentTimeMillis() - start); // 0
      }
    
  • StringBuilder提供了丰富而全面的方法,包括insert()replace()subString()reverse(),但最常用的还是append()toString()。还有delete()方法,删除指定位置坐标的内容。
  • StringBuilderStringBufferStringBuffer是线程安全的,StringBuilder速度更快

无意识的递归

  • ArrayList.toString(),它会遍历ArrayList中包含的所有对象,调用每个元素上的toString()方法

String上的操作

  • StringStringBufferStringBuilder实现了CharSequence接口,可实现通过类似于数组的一些操作
    public interface CharSequence {
          
          
        int length();
        char charAt(int index);
        default boolean isEmpty() {
          
          
            return this.length() == 0;
        }
        CharSequence subSequence(int start, int end);
        public String toString();
        public default IntStream chars() {
          
          
            ...
        }
        public default IntStream codePoints() {
          
          
            ...
        }
        @SuppressWarnings("unchecked")
        public static int compare(CharSequence cs1, CharSequence cs2) {
          
          
            ...
        }
    }
    
  • getChars():复制String的部分内容,到一个char数组上
      public static void main(String[] args) {
          
          
        String str = "fagdgadg";
        char[] chars = new char[10];
        str.getChars(1,3, chars, 2);
        System.out.println(Arrays.toString(chars));  // [ ,  , a, g,  ,  ,  ,  ,  ,  ]
    }
    
  • equalsIgnoreCase():忽略大小写比较两个String是否相等
  • CompareTo():按照词典顺序比较String的内容。
  • contentEquals:也是用于比较的方法,可以输入StringBuffer以及所有实现了CharSequence接口的类
  • regionMatcher():比较指定区域是否相等
    String a = "abcdefghijklmn";
    // 字符串的开始索引,另一个字符串,另一个字符串的开始索引,子字符串的长度
    System.out.println(a.regionMatches(2, "cdefghijklmn", 0, 12));
    
  • trim():将String两端的空白字符删除,返回一个新的String对象
  • valueOf:返回一个参数的字符串,比如int类型、double类型
    String s = String.valueOf(1.2324);
    
  • intern():字符串在常量池都有一个唯一的字符串,通过intern()取出该字符串在常量池中保存的字符串的引用
    String s0 = "123456";
    String s1 = new String("123456");
    String s2 = "123" + "456";
    String s3 = new String("123") + "456";
    
    System.out.println(s0 == s1);  // false
    System.out.println(s0.intern() == s1.intern());   //true
    
    System.out.println(s0 == s2);  //true
    System.out.println(s0.intern() == s2.intern());  //true
    
    System.out.println(s0 == s3);  //false
    System.out.println(s0.intern() == s3.intern());  //true
    

格式化输出

  • System.out.format:格式化输出字符串
    // +表示右对齐,15代表占用15个位置,5代表保留字符串前5个字符
    System.out.format("%15.5s", "nihaoshijie");  // "          nihao"
    System.out.println();
    // - 表示左对齐
    System.out.format("%-15.5s", "nihaoshijie"); // "nihao          "
    
  • Formatter类:Formatter看作一个翻译器,它将格式化字符串与数据翻译成需要的结果。在创建Formatter类中,需要给构造器传递一些信息,告诉它输出到哪里(流或者文件之类的)
    public class FormatterTest {
          
          
      private String name;
      private Formatter f;
    
      public FormatterTest(String name, Formatter f) {
          
          
        this.name = name;
        this.f = f;
      }
    
      public void move(int x, int y) {
          
          
        f.format("%s The Turtle is as (%d, %d)\n", name, x, y);
      }
    
      public static void main(String[] args) {
          
          
        PrintStream out = System.out;  // System.out的引用
        FormatterTest tommy = new FormatterTest("Tommy", new Formatter(out));
        FormatterTest terry = new FormatterTest("Terry", new Formatter(System.out));
        tommy.move(0, 0);
        terry.move(4, 8);
        tommy.move(3, 4);
        terry.move(2, 5);
        tommy.move(3, 3);
        terry.move(3, 3);
      }
    }
    
  • 格式化说明符:控制空格与对齐
    %[argment_index$][flags][width][.precision]conversion
    
    flag:控制左对齐(-)还是右对齐(+或无)
    • width:控制一个域的最小尺寸,默认不足的补空格,也可以自己指定
    • .precision:控制输出字符的最大数量,应用于浮点数时,它表示小数部分显示出来的位数;应用于String的时候,它表示打印出来String输出字符的最大数量
    public class Receipt {
          
          
      private double total = 0;
      private Formatter f = new Formatter(System.out);
    
      public void printTitle() {
          
          
        f.format("%-15s %5s %10s\n", "Item", "Qty", "Price");
        f.format("%-15s %5s %10s\n", "----", "---", "-----");
      }
    
      public void print(String name, int qty, double price) {
          
          
        f.format("%-15.15s %5d %10.2f\n", name, qty, price);
        total += price;
      }
    
      public void printTotal() {
          
          
        f.format("%-15s %5s %10.2f\n", "Tax", "", total * 0.06);
        f.format("%-15s %5s %10s\n", "", "", "-----");
        f.format("%-15s %5s %10.2f\n", "Total", "", total * 1.06);
      }
    
      public static void main(String[] args) {
          
          
        Receipt receipt = new Receipt();
        receipt.printTitle();
        receipt.print("Jack's Magic Beans", 4, 4.25);
        receipt.print("Princess", 3, 5.1);
        receipt.print("Three Bears Porridge", 1, 14.29);
        receipt.printTotal();
      }
    }
    /* output
    Item              Qty      Price
    ----              ---      -----
    Jack's Magic Be     4       4.25
    Princess            3       5.10
    Three Bears Por     1      14.29
    Tax                         1.42
                               -----
    Total                      25.06
    */
    
  • Formatter转换
    字符 说明
    d 整数型(十进制)
    c unicode字符
    b Boolean值
    s String
    f 浮点数(十进制)
    e 浮点数(科学计数)
    x 整数(十六进制)
    h 散列码(十六进制)
    % 字符"%"
    public class Conversion {
          
          
      public static void main(String[] args) {
          
          
        Formatter f = new Formatter(System.out);
    
        char u = 'a';
        System.out.println("u = 'a'");
        f.format("s: %s\n", u);  // 转换成字符串, "a"
        f.format("c: %c\n", u);  // 字符,'a'
        f.format("b: %b\n", u);  // true
        f.format("h: %h\n", u);  // 'a'在ASCII码中的十六进制表示,61
    
        int v = 121;
        System.out.println("v = 121");
        f.format("d: %d\n", v);  // 十进制, 121
        f.format("c: %c\n", v);  // 转换成字符,y,ASCII码十进制第121个为y
        f.format("b: %b\n", v);  // true
        f.format("s: %s\n", v);  // "121"
        f.format("x: %x\n", v);  // 十六进制,79
        f.format("h: %h\n", v);  // 散列码,79
    
        BigInteger w = new BigInteger("50000000000");
        System.out.println("w = new BigInteger(\"50000000000\")");
        f.format("d: %d\n", w);  // 50000000000
        f.format("b: %b\n", w);  // true
        f.format("s: %s\n", w);  // 50000000000
        f.format("x: %x\n", w);  // ba43b7400
        f.format("h: %h\n", w);  // a43b7555
    
        double x = 179.543;
        System.out.println("x = 179.543");
        f.format("b: %b\n", x);  // true
        f.format("s: %s\n", x);  // 179.543
        f.format("f: %f\n", x);  // 179.543000  (默认小数点后6位)
        f.format("e: %e\n", x);  // 1.795430e+02
        f.format("h: %h\n", x);  // 1ef462c
    
        Conversion y = new Conversion();  // 类的实例化对象本身
        System.out.println("y = new Conversion()");
        f.format("b: %b\n", y);  // true
        f.format("s: %s\n", y);  // chapter13.Conversion@cc34f4d
        f.format("h: %h\n", y);  // cc34f4d
    
        boolean z = false;
        System.out.println("z = false");
        f.format("b: %b\n", z);  // false
        f.format("s: %s\n", z);  // false
        f.format("h: %h\n", z);  // 4d5
      }
    }
    
  • String.format():接受与Formatter相同的参数,但是返回一个String对象
    其实在String.format内部,它也是创建一个Formatter对象,然后将你传入的参数转给该Formatter
    String s = String.format("%-15.5s, %.2f", "nihaoshijie", 12.5668945);
    System.out.println(s);   // "nihao          , 12.57"
    
    public class Hex {
          
          
      public static void main(String[] args) {
          
          
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < 100; i++) {
          
          
          s.append(String.format("%02X ", i));
          if (i % 10 == 9) s.append("\n");
        }
        System.out.println(s.toString());
      }
    }
    

正则表达式

  • 使用正则表达式,我们能够以编程的方式,构造复杂的文本模式,并对输入的字符串进行搜索。一旦找到了匹配这些模式的部分,你就能够随心所欲地对它们进行处理。

    public class IntegerMatch {
          
          
      public static void main(String[] args) {
          
          
        System.out.println("-1234".matches("-?\\d+"));  // 一个或零个"-",一个或多个数字
        System.out.println("+991".matches("(-|\\+)?\\d+"));  // 一个或零个符号"-"或"+",一个或多个数字
      }
    }
    
  • 转义字符\\,表示一个数字\\d,表示一个反斜线\\\\

  • split()replace(),可通过正则表达式匹配

    public class splitTest {
          
          
      public static void main(String[] args) {
          
          
        String s = "the, next, then, when, what, why";
        System.out.println(Arrays.toString(s.split(" ")));  // [the,, next,, then,, when,, what,, why]
        System.out.println(Arrays.toString(s.split("\\W+")));  // [the, next, then, when, what, why]
      }
    }
    
  • 创建正则表达式

    字符 含义
    B 指定字符B
    \xhh 十六进制为0xhh的字符
    \uhhhh 十六进制表示为0xhhhh的Unicode字符
    \t 制表符Tab
    \n 换行符
    \r 回车
    \f 换页
    \e 转义
    字符类 含义
    . 任意字符
    [abc] 包含abc的任何字符(和a|b|c作用相同)
    [^abc] 除了abc的任何字符
    [a-zA-Z] az或从AZ的任意字符
    [abc[hij]] 任意abchij的字符
    [a-z&&[hij]] az的范围内,满足hij的任意字符(两个条件的交集)
    \s 空白符(空格、tab、换行、换页、回车)
    \S 非空白符
    \w 词字符[a-zA-Z0-9]
    \W 非词字符[^\w]
    \d 数字[0-9]
    \D 非数字
    逻辑操作符 说明
    XY Y跟在X后面
    X|Y XY
    (X) 捕获组
    边界匹配符 说明
    ^ 一行的起始
    $ 一行的结束
    \b 词的边界
    \B 非词的边界
    \G 前一个匹配的结束
    System.out.println(s.matches("[rR]udolph"));
    System.out.println(s.matches("[rR][aeiou][a-z]ol.*"));  // `.*`表示后面追加任意字符(0或多个)
    System.out.println(s.matches("R.*"));
    
  • 量词:描述一个模式吸收输入文本的方式
    – 贪婪型:量词总是贪婪的,贪婪表达式会为所有可能的模式发现尽可能多的匹配。
    – 勉强型:用问号来指定,这个量词匹配满足模式所需的最少字符数。
    – 占有型:只有Java有,比较少用

    贪婪型 勉强型 如何匹配
    X? X?? 一个或零个X
    X* X*? 零个或多个X
    X+ X+? 一个或多个X
    X{n} X{n}? 恰好n个X
    X{n,} X{n,}? 至少n个X
    X{n,m} X{n,m}? 至少n个X,且不超过m次
  • PatternMatcher
    根据String类型的正则表达式生成一个Pattern对象,将想要检索的的字符串传入Pattern对象的matcher()方法,再生成一个Matcher对象。
    matcher()中有许多匹配的方法:matches()lookingAt()find()
    matches():用来判断整个输入字符串是否匹配正则表达式模式
    lookingAt():用来判断该字符串的开始部分是否能够匹配模式

    public class PatternTest {
          
          
      public static void main(String[] args) {
          
          
        args = new String[]{
          
          "abcabcabcdefabc", "abc+", "(abc)+", "(abc){2,}"};
        if (args.length < 2) {
          
             // 如果小于2,则直接退出
          System.exit(0);
        }
        System.out.println("Input: " + args[0]);
        for (String arg : args) {
          
          
          System.out.println("Regular expression: " + arg);
          Pattern p = Pattern.compile(arg);  // 根据正则表达式生成一个Pattern
          Matcher matcher = p.matcher(args[0]);   // 输入要被匹配的字符串,生成各种匹配结果的Matcher
          while (matcher.find()) {
          
            // 查找下一个匹配的,直到找不到
            System.out.println("Match \"" + matcher.group() + "\" at position " + matcher.start() + "-" + matcher.end());
          }
          System.out.println(matcher.matches());  // 尝试整个正则表达式进行匹配,返回boolean类型
          System.out.println(matcher.lookingAt());  // 尝试将输入序列与模式匹配,从区域的开头开始。返回boolean类型
        }
      }
    }
    
    public class Finding {
          
          
      public static void main(String[] args) {
          
          
        Pattern compile = Pattern.compile("\\w+");  // 所有单词
        Matcher m = compile.matcher("Evening is full of the linnet's wings");
        while (m.find()) {
          
           // 逐个查找,直到找不到匹配的了
          System.out.print(m.group() + " ");
        }
        System.out.println();
        int i = 0;
        while (m.find(i)) {
          
             // 频繁重新设置查找开始点
          System.out.print(m.group() + " ");
          i++;
        }
      }
    }
    
  • Groups:组是用括号划分的正则表达式,可以根据组的编号来引用某个组
    public int grouopCount()返回该匹配器的模式中的分组数目
    public String group()返回前一次匹配操作的第0组
    public String group(int i)返回在前一次匹配操作期间指定的组号的内容,如果没有匹配输入字符串的任何部分,则将会返回null
    public int start(int group)返回在前一次匹配操作中寻找到的组的起始索引
    public int end(int group)返回在前一次匹配操作中寻找到的组的最后一个字符索引加一的值

    public class Groups {
          
          
      static public final String POEM = "Twas brillig, and the slithy toves\n" +
              "Did fadf fafafj faklfer jkju jkl\n" +
              "af 34 54536 ,6563 244\n" +
              "44 242  24543, g dg sf\n" +
              "254 da,  gsf  fgsfa";
    
      public static void main(String[] args) {
          
          
        Matcher m = Pattern.compile("(?m)(\\S+)\\s+(\\S+)\\s+(\\S+)$").matcher(POEM);  // 寻找所有句子的最后三个词
        while (m.find()) {
          
          
          // m.group有4个元素,组号为0表示整个表达式,组号1表示被第一个括号扩起的组,以此类推
          for (int i = 0; i <= m.groupCount(); i++) {
          
          
            System.out.print("[" + m.group(i) + "]");
          }
          System.out.println();
        }
      }
    }
    
  • start()end()
    start()返回先前匹配的起始位置的索引,end返回所匹配的最后字符的索引加一的值。匹配操作失败之后,调用start()end()将会产生IllegalStateException

    public class StartEnd {
          
          
      public static String input = "As long as there is injustice, whenever a\n" +
              "Targathian baby cries out, wherever a distress\n" +
              "signal sounds among the stars ... We'll be there.\n" +
              "This fine ship, and this fine crew ... \n" +
              "Never give up! Never surrender!";
    
      private static class Display {
          
          
        private boolean regexPrinted = false;
        private String regex;
    
        public Display(String regex) {
          
          
          this.regex = regex;
        }
    
        void display(String message) {
          
          
          if (!regexPrinted) {
          
          
            System.out.println(regex);
            regexPrinted = true;
          }
          System.out.println(message);
        }
      }
    
      static void examine(String s, String regex) {
          
          
        Display d = new Display(regex);
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(s);
        while (m.find()) {
          
          
          d.display("find() '" + m.group() + "' start = " + m.start() + " end = " + m.end());
        }
        if (m.lookingAt()) {
          
             // 没有重置
          d.display("lookingAt() start = " + m.start() + " end = " + m.end());
        }
        if (m.matches()) {
          
            // 没有重置
          d.display("matches() start = " + m.start() + " end = " + m.end());
        }
      }
    
      public static void main(String[] args) {
          
          
        for (String in : input.split("\n")) {
          
          
          System.out.println("input : " + in);
          for (String regex : new String[]{
          
          "\\w*ere\\w*", "\\w*ever", "T\\w+", "Never.*?!"}) {
          
          
            examine(in, regex);
          }
        }
      }
    }
    /* output
    input : As long as there is injustice, whenever a
    \w*ere\w*
    find() 'there' start = 11 end = 16
    \w*ever
    find() 'whenever' start = 31 end = 39
    input : Targathian baby cries out, wherever a distress
    \w*ere\w*
    find() 'wherever' start = 27 end = 35
    \w*ever
    find() 'wherever' start = 27 end = 35
    T\w+
    find() 'Targathian' start = 0 end = 10
    lookingAt() start = 0 end = 10
    input : signal sounds among the stars ... We'll be there.
    \w*ere\w*
    find() 'there' start = 43 end = 48
    input : This fine ship, and this fine crew ... 
    T\w+
    find() 'This' start = 0 end = 4
    lookingAt() start = 0 end = 4
    input : Never give up! Never surrender!
    \w*ever
    find() 'Never' start = 0 end = 5
    find() 'Never' start = 15 end = 20
    lookingAt() start = 0 end = 5
    Never.*?!
    find() 'Never give up!' start = 0 end = 14
    find() 'Never surrender!' start = 15 end = 31
    lookingAt() start = 0 end = 14
    matches() start = 0 end = 31
    */
    

    注意:find()可以在输入的任意位置定位正则表达式,而lookingAt()matches()只有在正则表达式与输入的最开始处就开始匹配时才会成功。

  • split()
    将输入字符串断开成字符串对象数组

    //  通过Pattern分割
      @Test
      public void test() {
          
          
        String input = "This!!unusual use!!of exclamation!!points";
        Pattern c = Pattern.compile("!!");
        String[] split = c.split(input);
        System.out.println(Arrays.toString(split));
    
        // 限制分割字符串的个数
        String[] split1 = c.split(input, 3);
        System.out.println(Arrays.toString(split1));
      }
    
  • 替换操作
    replaceFirst(String replacement)以参数字符串replacement替换掉第一个匹配成功的部分
    replaceAll(String replacement)以参数字符串replacement替换掉所有匹配成功的部分
    appendReplacement(StringBuilder sbuilder, String replacement)执行渐进式的替换。实现执行一次或多次appendReplacement()之后,调用此方法可以将输入字符串余下的部分复制到sbuf

    public class replaceTest {
          
          
      @Test
      public void test() {
          
          
        String s = "agfkrjqekrhkfjfkjfkfhjfnfnvfjalkjdfklflwuirhnvn";
        Matcher m = Pattern.compile("[aeoui]").matcher(s);
        StringBuilder sbud= new StringBuilder();
        while (m.find()) {
          
          
          m.appendReplacement(sbud, m.group().toUpperCase());
        }
        System.out.println(sbud);  // AgfkrjqEkrhkfjfkjfkfhjfnfnvfjAlkjdfklflwUI
        m.appendTail(sbud); // AgfkrjqEkrhkfjfkjfkfhjfnfnvfjAlkjdfklflwUIrhnvn
        System.out.println(sbud);
      }
    }
    

    m.appendReplacement一点一点地替换,并且把结果一点一点地append()StringBuilder中。到最后,将剩余部分也加入到StringBuilder

  • reset()

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

扫描输入

  • Scanner的构造器可以接受任何类型的输入对象,包括File对象、InputStreamString或者其他Readable对象。
  • 所有的输入、分词以及翻译的操作都隐藏在不同类型的next方法中。普通的next()方法返回下一个String,所有的基本类型(除char之外)都有相应的next()方法,如nextInt()等。hasNext()判断下一个输入分词是否所需的类型,如hasNextInt()
  • Scanner定界符:在默认的情况下,Scanner根据空白字符对输入进行分词,但自己可以通过正则表达式指定所需的定界符
    public static void main(String[] args) {
          
          
        Scanner stdin = new Scanner(System.in);
        stdin.useDelimiter("\\s*,\\s*");  // 修改分隔符
        System.out.println(stdin.delimiter());  // 查询当前的分隔符,`\s*,\s*`
        while (stdin.hasNextInt()) {
          
          
          System.out.println(stdin.nextInt());
        }
      }
    
  • 用正则表达式扫描
    public class ThreatAnalyzer {
          
          
      static String threatAnalyzer = """
              58.27.82.161@02/10/2005
              204.45.234.40@02/11/2005
              58.27.82.161@02/12/2005
              """;
    
      public static void main(String[] args) {
          
          
        Scanner scanner = new Scanner(threatAnalyzer);
        Pattern p = Pattern.compile("(\\d+[.]\\d+[.]\\d+[.]\\d+)@(\\d{2}[/]\\d{2}/\\d{4})");
        while (scanner.hasNext(p)) {
          
            // 查看下一个是否符合要求
          scanner.next(p);  // 获取下一个
          MatchResult match = scanner.match();  // 将匹配结果返回
          String ip = match.group(1);   // 第一组,即ip
          String date = match.group(2);  // 第二组,即日期
          System.out.format("Threat on %s from %s\n", date, ip);
        }
      }
    }
    

猜你喜欢

转载自blog.csdn.net/weixin_42524843/article/details/113887376
今日推荐