【Java】文字列操作

標準ライブラリ(標準で使えるクラス)についてのまとめです

#ラッパークラス
* 基本型(値)のデータを包んでオブジェクトとしての機能を付与するクラス
* Boolean,Byte,Character,Double,Float,Integerなど
* **オートボクシング**
    * **ボクシング**：基本型からラッパーオブジェクトへの変換を暗黙的に行う <br>  `Integer int_obj = 108;`<br>  //明示的に変換する場合 `Long value = Long.valueOf(10);`
    * **アンボクシング**：ラッパーオブジェクトから基本型への変換を暗黙的に行う<br> `int i = int_obj;`<br>  //明示的に変換する場合 `long num = value.longValue();`
* 内部的にはオブジェクトの生成・破棄が発生するので、ループの中で安易に使わないようにしたい。まずは基本型を優先。

```Java
public static void main(String[] args) {
  //正しくはlong
  Long result = 0L;

  for (var i = 1; i < 10000; i++) {
    //ここでアンボクシング(加算)＋ボクシング(代入)が毎回発生！！
    result += i;
  }
  System.out.println(result);
}
```

#文字列と数値の変換
* **parseIntメソッド**：型に応じて文字列をInt,Longに変換。第二引数に基数を指定
* **valueOfメソッド**(Stringクラス)：数値を文字列に変換。
* **toStringメソッド**(ラッパークラス)：数値を文字列に変換。第二引数でn進数文字列を指定

```java
public class Main {
  public static void main(String[] args) {
    System.out.println(Integer.parseInt("108")); //108
    System.out.println(Double.parseDouble("1.2345")); //1.2345
    System.out.println(Integer.parseInt("FF", 16)); //255
    System.out.println(Double.parseDouble("0.653e2")); //65.3
  }
}
```

```java
public class Main {
  public static void main(String[] args) {
    System.out.println(String.valueOf(108)); //108
    System.out.println(Double.toString(1.2345)); //1.2345
    System.out.println(Integer.toString(255, 16)); //ff
    System.out.println(Integer.toHexString(255)); //ff
  }
}
```

#文字列の操作(Stringクラス)
* 文字列リテラル=Stringクラスのインスタンスを生成する
* Stringクラスは文字列操作の機能を提供する
* **文字列インスタンスは文字列リテラル（”nekko”）で生成**
* →コンストラクタ（new String(...)）で生成してしまうと文字列リテラルでインスタンス生成後に再度コンストラクタでインスタンスを生成してしまう<br> NG例：`var str = new String(“nekko”);`

#文字列の長さ
* **lengthメソッド**で取得
    * サロゲートペア(4byte文字)に注意　例：“叱”
    * lengthメソッドでは識別できないので2文字でカウントしてしまう
    * →**codePointCountメソッド**を使う
    * 引数は`(int begin, int end)` //開始位置,終了位置　(省略不可)

```java
public class Main {
  public static void main(String[] args) {
    var str = "𠮟る";
    System.out.println(str.codePointCount(0, str.length())); //2
  }
}
```

#文字列比較
* **compareTo**：辞書的な大小比較
    * cf：equalsは等しいかどうかのみ比較
* **compareTolgnoreCase**：大文字区別なし

```java
public class Main {
	public static void main(String[] args) {
		String str1 = "a";
		String str2 = "b";
		String str3 = "A";
		System.out.println(str1.compareTo(str2)); //-1
		System.out.println(str2.compareTo(str1)); //1
		System.out.println(str1.compareTo(str1)); //0
		System.out.println(str1.compareTo(str3)); //32
		System.out.println(str1.compareToIgnoreCase(str3)); //0
	}
}
```

#空判定
* **isEmptyメソッド**
* **isBlankメソッド**：空文字列か空白を判定する(Java11以降)
    * 空白=半角全角スペース,タブ(\t),改行(\n,\r,\f)

```java
public class Main {
  public static void main(String[] args) {
    var str1 = "";
    var str2 = " 　  ";
    System.out.println(str1.isEmpty()); //true
    System.out.println(str1.isBlank()); //true
    System.out.println(str2.isEmpty()); //falsa
    System.out.println(str2.isBlank()); //true
  }
}
```

#空白除去
* **stripメソッド**：文字列**前後**の空白除去
    * **stripLeading**：文字列前方のみ除去(Java11以降)
    * **stripTrailing**：文字列後方のみ除去(Java11以降)
    * 以前はtrimLeft/trimRightが相当、全角スペースは認識しない

```java
public class Main {
  public static void main(String[] args) {
    var str = "  Neko　 Mohu Mohu  ";
    System.out.println(str.strip());          //Neko　 Mohu Mohu
    System.out.println(str.stripLeading());   //Neko　 Mohu Mohu  
    System.out.println(str.stripTrailing());  //  Neko　 Mohu Mohu
  }
}
```

#文字列位置検索
* **indexOf**：特定の文字列を前方検索
    * 見つかったら**先頭文字位置**を返す。見つからなかったら-1
    * 第二引数に**検索開始位置**を指定
* **lastIndexOf**：特定の文字列を後方検索
    * 後方検索でも戻り値は**先頭からの文字位置**

```java
public class Main {
  public static void main(String[] args) {
    var str = "にわにはにわにわとりがいる";
    System.out.println(str.indexOf("にわ")); //0
    System.out.println(str.indexOf("にも")); //-1
    System.out.println(str.lastIndexOf("にわ")); //6
    System.out.println(str.indexOf("にわ", 3)); //4
    System.out.println(str.lastIndexOf("にわ", 3)); //0
  }
}
```

#文字列含有検索
* **containsメソッド**
* **startsWith**：文字列がある文字で始まる
* **endsWith**：文字列がある文字で終わる
    * 第二引数で検索開始位置を指定

```java
  public static void main(String[] args) {
    var str = "猫もふりたい";
    System.out.println(str.contains("猫")); //true
    System.out.println(str.startsWith("猫")); //true
    System.out.println(str.startsWith("もふ", 3)); //false
    System.out.println(str.endsWith("たい")); //true
    System.out.println(str.indexOf("もふ") > 0); //true
    System.out.println(str.indexOf("猫") == 0); //true
  }
}
```

#部分文字列取得
* **substringメソッド**
    * 引数は (begin,end)→begin~end-1の文字列を抽出
* **charAtメソッド**：一文字抽出

```java
var str = "猫もふりたい";
System.out.println(str.substring(1, 4)); //もふり
```

```java
public static void main(String[] args) {
    var mail = "neko@example.com";
    System.out.println(mail.substring(mail.lastIndexOf("@") + 1)); //example.com
    System.out.println(mail.charAt(0)); //n
    
    for (var i = 0; i < mail.length(); i++) {
     System.out.println(mail.charAt(i)); //全文字抽出
    }
}
```

# 区切り文字で分割
* **splitメソッド**
    * 第一引数：区切り文字、第二引数：最大分割数
* **joinメソッド**：特定の区切り文字で連結する
    * 第一引数：区切り文字、第二引数：連結文字(可変長)

```java
public class Main {
  public static void main(String[] args) {

    var str1 = "neko,inu,usa";
    var result1 = str1.split(",");
    System.out.println(String.join("＆", result1)); //neko＆inu＆usa

    //正規表現でどちらかの文字で分割
    var str2 = "neko,inu,usaとtori";
    var result2 = str2.split("[,と]");
    System.out.println(String.join("＆", result2)); //neko＆inu＆usa＆tori

    //文字単位で全分割
    var str3 = "neko,inu,usa";
    var result3 = str3.split("");
    System.out.println(String.join("＆", result3)); //n＆e＆k＆o＆,＆i＆n＆u＆,＆u＆s＆a

    //最大2分割
    var str4 = "neko,inu,usa,tori";
    var result4 = str4.split(",", 2);
    System.out.println(String.join("＆", result4)); //neko＆inu,usa,tori

    System.out.println(String.join(",", "neko", "inu", "usa")); //"neko,inu,usa"
  }
}
```	

#文字列整形
* **formatメソッド**
    * Placeholder：引数argsで指定した文字列を埋め込む場所
    * 書式指定子：書式文字列の中に値を埋めこむための記法
    * 書式指定子以外はそもまま出力
        * 書式指定子の例<br> `System.out.println(String.format("%sの体重はりんご%d個分です。","アルクマ",70)); `
    * 1\$,2\$：1,2番目の引数を表す
    * %.2s：文字列の最小幅指定
    * %8s：文字列の幅指定。不足した分は左が空白。負数で右空白
    * %#x：　接頭辞に0/0xをつけて16進数表示
    * %.2e：小数点以下の2桁表示、eで指数表記
    * %tF：日付 %tY-%tm-%td と同じ
    * %td：時刻
* **printfメソッド**：formatの代わりにそのまま表示。改行文字は付与しない

```java
import java.util.*;
import java.time.LocalDateTime;
import java.util.Locale;

public class Main {
 public static void main(String[] args) {
    System.out.println(String.format("%sの体重はりんご%d個分です。","アルクマ",70)); 
    System.out.println(String.format("%1$sの体重はりんご%3$d個分です。%1$sは元気です。","アルクマ","キティ",70)); //アルクマの体重はりんご70個分です。アルクマは元気です。
    //文字列の最小幅指定
    System.out.println(String.format("%.2sです。", "アルクマ"));       //アルです。
    //文字列の幅指定
    System.out.println(String.format("%8sです。", "アルクマ"));        //    アルクマです。
    System.out.println(String.format("%-5sです。", "アルクマ"));       //アルクマ です。
    //最大桁数指定
    System.out.println(String.format("10進数 %08d", 12345));          //10進数 00012345
    //16進数
    System.out.println(String.format("16進数 %#x", 10));              //16進数 0xa
    //小数点以下の桁数指定
    System.out.println(String.format("小数点数 %.2f", 123.456));      //小数点数 123.46
    //指数
    System.out.println(String.format("指数／小文字 %.2e", 123.456));  //指数／小文字 1.23e+02
    System.out.println(String.format("指数／大文字 %.2E", 123.456));  //指数／大文字 1.23E+02
    //日時
    var d = LocalDateTime.now();
    System.out.println(String.format("%tF", d));                      //2020-10-31
    System.out.println(String.format("%tY-%tm-%td", d,d,d));          //2020-10-31
    System.out.println(String.format("%tr", d));                      //03:42:23 AM
    System.out.println(String.format("%1$tY年 %1$tm月 %1$td日", d));  //2020年 10月 31日
    //小数点,表記
    System.out.println(String.format(Locale.GERMAN, "%f", 1234.567)); //1234,567000
    //printfメソッドで表示
    System.out.printf("%.2sです。\n", "アルクマ");  
  }
}
```