各種言語のBase64文字列の取り扱い

# はじめに
各種システムとのデータ連携にあたってBase64文字列を用いるシーンが偶にあり、各言語でどういった実装をするのかを毎回悩むため整理してみました。（※サンプルコードはdockerにて動作確認済み）

# 各言語の実装

## 1.PHP

```php:main.php
<?php

$text="{'name':'山田太郎','address':'東京都港区'}";
echo base64_encode($text); # eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9
echo "\n";

$text = 'eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9';
echo base64_decode($text); # {'name':'山田太郎','address':'東京都港区'}
echo "\n";

?>
```


```shell
$ docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp php:8.2-cli php main.php
```


## 2.java

```java:Main.java
import java.util.Base64;

public class Main {
    public static void main(String[] args){
        String text = "{'name':'山田太郎','address':'東京都港区'}";
        System.out.println(Base64.getEncoder().encodeToString(text.getBytes()));
        // eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9

        text = "eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9";
        System.out.println(new String(Base64.getDecoder().decode(text)));
        // {'name':'山田太郎','address':'東京都港区'}
    }
}
```

```shell
docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp openjdk:11 bash -c 'javac Main.java; java Main; rm *.class;'
```

## 3.python


```python:main.py
import base64

text = "{'name':'山田太郎','address':'東京都港区'}"
print(base64.b64encode(text.encode()).decode()) 
# eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9

text = "eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9"
print(base64.b64decode(text).decode())
# {'name':'山田太郎','address':'東京都港区'}

```

```
docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:3.13.0a2 python main.py
```


## 4.javascript

### 4-1.atobとbtoaを用いる方法　Part1
ブラウザ上およびnode.jsで動作する方法。

■参考　[The "Unicode Problem" | MSDN](https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem)  

```javascript:main.js
function encodeBase64(str) {
  const bytes = new TextEncoder().encode(str);
  const binString = String.fromCodePoint(...bytes);
  return btoa(binString);
}

function decodeBase64(base64) {
  const binString = atob(base64);
  return new TextDecoder().decode(Uint8Array.from(binString, (m) => m.codePointAt(0)));
}

let text = "{'name':'山田太郎','address':'東京都港区'}";
console.log(encodeBase64(text));
// eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9

text = "eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9";
console.log(decodeBase64(text));
// {'name':'山田太郎','address':'東京都港区'}
```

```
docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp node:21.4.0 node main.js
```


### 4-2.atobとbtoaを用いる方法　Part2
ブラウザ上およびnode.jsで動作する方法。※古いやり方。

■参考　[解決策その 1 - 文字列をエンコードする前にエスケープする | MSDN](https://developer.mozilla.org/ja/docs/Glossary/Base64#%E8%A7%A3%E6%B1%BA%E7%AD%96%E3%81%9D%E3%81%AE_1_-_%E6%96%87%E5%AD%97%E5%88%97%E3%82%92%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B%E5%89%8D%E3%81%AB%E3%82%A8%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%97%E3%81%99%E3%82%8B)  
※参考サイトではescapeとunescapeを非推奨としているので、以下サンプルでは自前関数で代替してます。

```javascript:main.js
const encodeBase64 = (str) => {
  let b = encodeURIComponent(str)
    .replace(/%.{2}/g, (match) => {
        return String.fromCharCode(parseInt(match.slice(1), 16));
    });
  return btoa(b);
}
const decodeBase64 = (str) => {
  let b = atob(str)
    .split('')
    .map((c) => {
      return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    })
    .join('');
  return decodeURIComponent(b);
}

let text = "{'name':'山田太郎','address':'東京都港区'}";
console.log(encodeBase64(text));
// eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9

text = "eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9";
console.log(decodeBase64(text));
// {'name':'山田太郎','address':'東京都港区'}

```

```
docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp node:21.4.0 node main.js
```


### 4-3.Bufferを用いる方法
node.js上で実行する場合はBufferを用いる方が簡単。
```javascript:main.js
let text = "{'name':'山田太郎','address':'東京都港区'}";
console.log(Buffer.from(text).toString('base64'));
// eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9

text = "eyduYW1lJzon5bGx55Sw5aSq6YOOJywnYWRkcmVzcyc6J+adseS6rOmDvea4r+WMuid9";
console.log(Buffer.from(text, 'base64').toString());
// {'name':'山田太郎','address':'東京都港区'}
```

```
docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp node:21.4.0 node main.js
```


# 終わりに
JWTなどではBase64ではなく、Base64Urlを用いることになるのでもう少し工夫が入りますが別の機会に。