(Java、JavaScript、Python)文字列処理の比較

# Java、JavaScript、Pythonの処理比較

最近JavaScriptとPythonの勉強を始めました。
学んだことの整理として、Java、JavaScript、Pythonの3言語の処理比較を記事にしていこうと思います。
今回は文字列処理です。

## 文字列処理の比較一覧

|処理内容|Java|JavaScript|Python|
|---|---|---|---|
|文字列長|length()|length|len(文字列)|
|一致比較|equals(比較文字)<br>equalsIgnoreCase(比較文字)|==比較演算子<br>===比較演算子|==比較演算子|
|検索|indexOf(検索文字列)<br>lastIndexOf(検索文字列)<br>contains(検索文字列)<br>startsWith(検索文字列)<br>endsWith(検索文字列)<br>|indexOf(検索文字列)<br>lastIndexOf(検索文字列)<br>includes(検索文字列)<br>startsWith(検索文字列)<br>endsWith(検索文字列)|find(検索文字列)<br>rfind(検索文字列)<br>in式<br>not in式<br>startswith(検索文字列)<br>endswith(検索文字列)|
|単純結合|＋演算子<br>concat(結合文字列)<br>StringBuilder.append(結合文字列)|＋演算子<br>concat(結合文字列,...)|＋演算子|
|空白除去|trim()<br>strip()|trim()<br>trimLeft()<br>trimRight()|strip(削除対象文字)<br>lstrip(削除対象文字)<br>rstrip(削除対象文字)|
|大文字小文字変換|toUpperCase()<br>toLowerCase()|toUpperCase()<br>toLowerCase()|upper()<br>lower()<br>capitalize()<br>title()<br>swapcase()<br>|
|切り出し|substring(開始index,終了index)<br>charAt(index)|substring(開始index,終了index)<br>charAt(index)<br>slice(開始index,終了index)<br>substr(開始index,文字数)<br>|文字列[開始index:終了index]|
|繰り返し|repeat(繰返し数)|repeat(繰返し数)|*演算子|
|置換|replace(前文字,後文字)<br>replaceFirst(前文字,後文字)<br>replaceAll(前文字,後文字)<br>|replace(前文字,後文字)<br>replaceAll(前文字,後文字)|replace(前文字,後文字)<br>re.sub(前文字,後文字,文字列)<br>re.subn(前文字,後文字,文字列)<br>translate(式)|
|区切りで結合|String.join(区切り文字,結合文字列...)|結合文字配列.join(区切り文字)|区切り文字.join(結合文字配列)|
|区切りで分割|split(区切り文字)<br>|split(区切り文字)|split(区切り文字)<br>rsplit(区切り文字)<br>splitlines()<br>re.split(区切り文字)|
|任意成型|String.format(成型式,文字列...)|テンプレートリテラル→<br>\`xxx${変数名}xxx\`|成型式.format(変数や式...)<br>f'xxx{変数や式}xxx'<br>形成式 % (変数や式,...)|

## 追記

- 一致比較
  - JavaのequalsIgnoreCase()は大文字小文字を無視した一致比較を行います。  
  - ===演算子は型を含めた厳密な比較を行います。'123'===123　→false
- 検索
  - 一致箇所の文字indexを返す：indexOf()、lastIndexOf()、find()、rfind()  
  - true/falseを返す：contains()、includes()、in式、not in式  
  - 先頭文字の一致判定、true/falseを返す：startsWith()  
  - 末尾文字の一致判定、true/falseを返す：endsWith()  
- 空白除去
  - いずれの言語も半角空白、タブ、改行が除去対象になります。  
  - Javaのstrip()は上記に加え、全角空白も除去対象になります。  
  - Pythonの関数は引数に削除対象文字指定可、省略時は空白・タブ・改行が除去対象になります。  
- 大文字小文字変換
  - Pythonは他の2つよりも細かい変換関数が存在します。capitalize()→最初の文字を大文字に他は小文字に変換、title()→単語最初の文字を大文字に他は小文字に変換、swapcase()→大文字小文字を入れ替え  
- 切り出し
  - substring()は終了indexを省略可能で、その場合は文字列の末尾を終了indexとします。  
  - JavaScriptの切り出しsubstring()とslice()は通常の使用範囲では差異はありませんが、引数がイレギュラーな場合に結果が異なります。  
  - 開始index＞終了indexの場合、substring()は自動で開始終了を入替え、slice()は空文字を返します。  
  - 負数指定の場合、substring()は引数を0として扱い、slice()は「文字列長－引数」として扱います。  
  - Pythonは関数ではなく「文字列[start:end]」という書式のスライスを使用します。start,endいずれか省略可能、「:」省略時はcharAt()のように1文字を取得します。  
- 置換
  - Java：replace()はマッチした全文字置換、replaceFirst()マッチした最初の文字置換。  
  - JavaScript：replace()はマッチした最初の文字置換、replaceAll()はマッチした全文字置換、ただしreplace()も正規表現オプション「g」で全文字置換可能。  
  - Python：replace()は第3引数にsplit()のように最大置換数指定可。  
  - Python：translate()は複数ケースの置換が可能。ただし置換元文字は1文字であること。置換の組み合わせを「str.maketrans()」で指定します。「"abCdefghIk".translate(str.maketrans({'C':'c', 'I':'ij'}))」  
  - 正規表現が使用できるのは各言語で以下の通りです。  
    - Java：replaceFirst()、replaceAll()  
    - JavaScript：replace()、replaceAll()  
    - Python:re.sub()、re.subn()、subnは置換処理された文字列＋変換した個数のリスト(正確にはタプル)を返します。  
- 区切りで分割  
  - split()はどの言語でも第2引数に最大分割数が指定可能です。  
  - Java、JavaScriptのsplit()は正規表現使用可能です。JavaScriptにおいては空白区切りは正規表現で「/\s/」と指定します。  
  - Pythonはreモジュールのre.split()が正規表現使用可能です。  
  - Pythonのsplit()は引数省略可能で、省略時は空白・タブ・改行が分割対象となります。  
  - Pythonのrsplit()は第2引数に最大分割数指定したとき右からカウントするのがsplit()との差異になります。  
  - Pythonのsplitlines()は改行で分割します。OS依存の改行にも対応しています。  
- 任意成型
  - Pythonはいくつかの書式があります。format()は下記サンプル参照。  
  - 1つは%演算子：sei='山田' mei='太朗'のとき、print('名前：%s %s' % (sei,mei))  
  - 1つはf文字列：print(f'名前：{sei} {mei}')  

## サンプル

### Java
```
public class StringSample {

    public static void main(String[] args) {
        StringSample obj = new StringSample();
        obj.executeSample();
    }

    public void executeSample() {
        String str = "  山田,太朗,Yamada,Taro,M,19950827,25才,東京都千代田区神田佐久間町2-XX-XX〇〇荘   ";

        // 空白除去
        String strKuhaku1 = str.trim();

        // 区切りで分割
        String[] strBun1 = strKuhaku1.split(",");

        // 単純結合
        String resName = new StringBuilder().append(strBun1[0]).append(strBun1[1]).toString();

        // 大文字小文字変換
        String strOoko1 = strBun1[2].toUpperCase();
        String strOoko2 = strBun1[3].toUpperCase();

        // 単純結合
        String resAlpha = strOoko1.concat(" ").concat(strOoko2);

        // 一致比較
        String resSex;
        if (strBun1[4].equals("M")) {
            resSex = "男性";
        } else {
            resSex = "女性";
        }

        // 切り出し
        String strKiri1 = strBun1[5].substring(0, 4);
        String strKiri2 = strBun1[5].substring(4, 6);
        String strKiri3 = strBun1[5].substring(6);

        // 区切りで結合
        String resBirth = String.join("/", strKiri1, strKiri2, strKiri3);

        // 置換
        String resAge = strBun1[6].replace("才", "歳");

        // 文字列長
        String resAddress = strBun1[7];
        if (16 < strBun1[7].length()) {
            resAddress = strBun1[7].substring(0, 16);
            // 単純結合
            resAddress = resAddress + "(略)";
        }

        // 検索
        String resMessage1 = "";
        String resMessage2 = "";
        if (strBun1[7].startsWith("東京都")) {
            resMessage1 = "首都です。";
        }
        if (strBun1[7].contains("千代田区")) {
            resMessage2 = "東京駅があります。";
        }

        // 任意成型
        String res1 = String.format("%s(%s)様 %s %s生(%s)", resName, resAlpha, resSex, resBirth, resAge);
        String res2 = String.format("%s %s%s", resAddress, resMessage1, resMessage2);

        // 繰り返し
        String line = "-".repeat(64);

        System.out.println(line);
        System.out.println(res1);
        System.out.println(res2);
        System.out.println(line);
    }
}
```

### JavaScript
```
<html>
<head>
<script>
function executeSample() {
        let str = "  山田,太朗,Yamada,Taro,M,19950827,25才,東京都千代田区神田佐久間町2-XX-XX〇〇荘   ";

        // 空白除去
        let strKuhaku1 = str.trim();

        // 区切りで分割
        let strBun1 = strKuhaku1.split(",");

        // 単純結合
        let resName = strBun1[0] + strBun1[1];

        // 大文字小文字変換
        let strOoko1 = strBun1[2].toUpperCase();
        let strOoko2 = strBun1[3].toUpperCase();

        // 単純結合
        let resAlpha = strOoko1.concat(" ", strOoko2);

        // 一致比較
        let resSex;
        if (strBun1[4] == "M") {
            resSex = "男性";
        } else {
            resSex = "女性";
        }

        // 切り出し
        let strKiri1 = strBun1[5].substring(0, 4);
        let strKiri2 = strBun1[5].substring(4, 6);
        let strKiri3 = strBun1[5].substring(6);

        // 区切りで結合
        let resBirth = [strKiri1, strKiri2, strKiri3].join("/");

        // 置換
        let resAge = strBun1[6].replace("才", "歳");

        // 文字列長
        let resAddress = strBun1[7];
        if (16 < strBun1[7].length) {
            resAddress = strBun1[7].substring(0, 16);
            // 単純結合
            resAddress = resAddress + "(略)";
        }

        // 検索
        let resMessage1 = "";
        let resMessage2 = "";
        if (strBun1[7].startsWith("東京都")) {
            resMessage1 = "首都です。";
        }
        if (strBun1[7].includes("千代田区")) {
            resMessage2 = "東京駅があります。";
        }

        // 任意成型
        let res1 = `${resName}(${resAlpha})様 ${resSex} ${resBirth}生(${resAge})`;
        let res2 = `${resAddress} ${resMessage1}${resMessage2}`;

        // 繰り返し
        let line = "-".repeat(64);

        console.log(line);
        console.log(res1);
        console.log(res2);
        console.log(line);
    }
</script>
</head>
<body onload="executeSample()">
  <h1>Qiita記事</h1>
  <h2>文字列処理</h2>
</body>
</html>
```

### Python
```
str1 = "  山田,太朗,Yamada,Taro,M,19950827,25才,東京都千代田区神田佐久間町2-XX-XX〇〇荘   "

# 空白除去
strKuhaku1 = str1.strip()

# 区切りで分割
strBun1 = strKuhaku1.split(",")

# 単純結合
resName = strBun1[0] + strBun1[1]

# 大文字小文字変換
strOoko1 = strBun1[2].upper()
strOoko2 = strBun1[3].upper()

# 単純結合
resAlpha = strOoko1 + " " + strOoko2

# 一致比較
resSex = ""
if strBun1[4] == "M":
    resSex = "男性"
else:
    resSex = "女性"

# 切り出し
strKiri1 = strBun1[5][0:4]
strKiri2 = strBun1[5][4:6]
strKiri3 = strBun1[5][6:]

# 区切りで結合
resBirth = "/".join([strKiri1, strKiri2, strKiri3])

# 置換
resAge = strBun1[6].replace("才", "歳")

# 文字列長
resAddress = strBun1[7]
if 16 < len(strBun1[7]):
    resAddress = strBun1[7][:16]
    # 単純結合 +
    resAddress = resAddress + "(略)"

# 検索
resMessage1 = ""
resMessage2 = ""
if strBun1[7].startswith("東京都"):
    resMessage1 = "首都です。"
if  "千代田区" in strBun1[7]:
    resMessage2 = "東京駅があります。"

# 任意成型
res1 = "{}({})様 {} {}生({})".format(resName, resAlpha, resSex, resBirth, resAge)
res2 = "{} {}{}".format(resAddress, resMessage1, resMessage2)

# 繰り返し *
line = "-" * 64

print(line)
print(res1)
print(res2)
print(line)
```

### 出力結果
```
----------------------------------------------------------------
山田太朗(YAMADA TARO)様 男性 1995/08/27生(25歳)
東京都千代田区神田佐久間町2-X(略) 首都です。東京駅があります。
----------------------------------------------------------------
```