# 機能設計書 31-文字列分割・結合

## 概要

本ドキュメントは、Julia Base ライブラリにおける文字列分割・結合機能の設計を記述する。`split` / `rsplit` / `join` / `eachsplit` / `chomp` / `chop` / `strip` / `lstrip` / `rstrip` 等の関数群により、文字列のトークン化・結合・トリミングを提供する。

### 本機能の処理概要

本機能は、文字列を区切り文字やパターンに基づいて分割し、または複数の文字列を結合する操作を提供するものである。

**業務上の目的・背景**：プログラミングにおいてテキストデータの解析・変換は極めて基本的な操作であり、CSV解析、ログ解析、自然言語処理、設定ファイルのパースなど多岐にわたる場面で文字列の分割・結合・トリミングが必要となる。Julia の文字列処理基盤として、他の全ての文字列操作の基礎となる機能である。

**機能の利用シーン**：ファイルからの行読み取り後のフィールド分割、ユーザー入力のトリミング（前後空白除去）、トークンリストの結合による文字列生成、改行文字の除去など。

**主要な処理内容**：
1. `split` / `rsplit` による文字列の区切り文字ベース分割（配列返却）
2. `eachsplit` / `eachrsplit` による遅延評価型イテレータベース分割
3. `join` による配列要素の区切り文字付き結合
4. `strip` / `lstrip` / `rstrip` による前後の空白・指定文字除去
5. `chomp` による末尾改行除去
6. `chop` / `chopprefix` / `chopsuffix` による先頭・末尾文字除去
7. `lpad` / `rpad` による文字列パディング
8. `rtruncate` / `ltruncate` / `ctruncate` による文字列切り詰め
9. `replace` による文字列置換（単一・複数パターン対応）

**関連システム・外部連携**：PCRE2 ライブラリ（正規表現パターンを区切り文字として使用する場合）。

**権限による制御**：該当なし。純粋な文字列操作関数であり、権限制御は不要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | REPL | 主画面 | 対話的な文字列操作 |

## 機能種別

文字列処理ユーティリティ（分割・結合・トリミング・パディング・置換）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| str | AbstractString | Yes | 操作対象の文字列 | 空文字列も許容 |
| dlm/splitter | AbstractString / AbstractChar / Regex / Function | No | 区切り文字・パターン（split系） | 省略時は isspace |
| limit | Integer | No | 最大分割数（split系） | 0以上（0は無制限） |
| keepempty | Bool | No | 空文字列を結果に含めるか | デフォルトは dlm 指定時 true、省略時 false |
| pat=>r | Pair | Yes(replace) | 検索パターンと置換文字列のペア | count < 0 で DomainError |
| head/tail | Integer | No | 除去する先頭/末尾の文字数（chop） | 0以上 |
| chars | Chars | No | 除去対象文字集合（strip系） | AbstractChar / Tuple / Vector / Set |
| n | Integer | Yes(pad) | パディング後の目標幅 | textwidth ベース |
| p | AbstractChar/AbstractString | No | パディング文字（pad系） | デフォルトはスペース |
| maxwidth | Integer | Yes(truncate) | 切り詰め後の最大幅 | 0以上 |
| replacement | AbstractString/AbstractChar | No | 切り詰め時の置換文字（truncate系） | デフォルトは '...' |

### 入力データソース

関数の引数として直接受け取る。ファイルやDBからの入力はない。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| split結果 | Vector{SubString{T}} | 分割された部分文字列の配列 |
| eachsplit結果 | SplitIterator | 遅延評価型の分割イテレータ |
| eachrsplit結果 | RSplitIterator | 右からの遅延評価型分割イテレータ |
| join結果 | String | 結合された文字列 |
| strip/chomp/chop結果 | SubString | トリミング後の部分文字列 |
| replace結果 | String / IO | 置換後の文字列（IO版は IO を返却） |
| pad結果 | String | パディング後の文字列 |
| truncate結果 | String | 切り詰め後の文字列 |

### 出力先

関数の戻り値として呼び出し元に返却する。`replace` の IO 版は指定された IO ストリームに書き込む。

## 処理フロー

### 処理シーケンス

```
1. split(str, splitter; limit, keepempty)
   └─ eachsplit で SplitIterator を生成し collect で配列化
2. eachsplit(str, splitter; limit, keepempty)
   └─ SplitIterator{S,F} を構築
   └─ iterate 時に findnext で次の区切り位置を検索
   └─ SubString で部分文字列を切り出し
3. rsplit(str, splitter; limit, keepempty)
   └─ eachrsplit で RSplitIterator を生成
   └─ collect して reverse! で順序を反転
4. strip(str) / lstrip(str) / rstrip(str)
   └─ lstrip: pairs(s) で先頭から走査し、述語が false になる位置で SubString 生成
   └─ rstrip: Iterators.reverse(pairs(s)) で末尾から走査
   └─ strip: rstrip 後に lstrip
5. chomp(str)
   └─ 末尾が \n なら除去、\r\n なら両方除去
   └─ String/SubString 版は codeunits ベースの最適化実装
6. replace(str, pat=>repl; count)
   └─ _replace_init でパターン検索を初期化
   └─ _replace_finish で逐次置換を実行
   └─ _replace_once で 1 回分の置換（argmin で最左マッチを選択）
```

### フローチャート

```mermaid
flowchart TD
    A[開始: split/eachsplit] --> B{区切り文字の型判定}
    B -->|AbstractChar| C[isequal でラップ]
    B -->|Tuple/Vector/Set| D[in でラップ]
    B -->|String/Regex/Function| E[そのまま使用]
    B -->|省略| F[isspace を使用]
    C --> G[SplitIterator 生成]
    D --> G
    E --> G
    F --> G
    G --> H{iterate 呼び出し}
    H --> I[findnext で区切り位置検索]
    I --> J{マッチあり?}
    J -->|Yes| K[SubString で部分文字列切り出し]
    K --> L{keepempty チェック}
    L -->|空でない or keepempty| M[yield SubString]
    L -->|空で keepempty=false| H
    M --> N{limit 到達?}
    N -->|No| H
    N -->|Yes| O[残り全体を返却]
    J -->|No| P[残り全体を返却して終了]
    O --> Q[終了]
    P --> Q
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-31-01 | デフォルト区切り文字 | dlm 省略時は isspace を使用 | split/eachsplit で dlm 未指定時 |
| BR-31-02 | keepempty デフォルト | dlm 指定時は true、省略時は false | split/eachsplit |
| BR-31-03 | limit=0 の解釈 | 分割数に上限なし | split/eachsplit |
| BR-31-04 | chomp の改行除去 | \n のみ、または \r\n の組を1回だけ除去 | chomp |
| BR-31-05 | replace 複数パターン | 左から走査し最左マッチを優先、同位置では引数順で優先 | replace に複数 Pair 指定時 |
| BR-31-06 | pad の textwidth | 文字幅は textwidth で計算（codepoint 数ではない） | lpad/rpad |
| BR-31-07 | SubString 返却 | strip/chomp/chop 系はコピーを避け SubString を返却 | strip/chomp/chop/chopprefix/chopsuffix |

### 計算ロジック

- `lpad`: パディング量 = n - textwidth(s)、パディング文字の幅で除算して繰り返し回数を算出
- `replace`: argmin(map(first, rs)) で最左マッチのパターンを選択

## データベース操作仕様

### 操作別データベース影響一覧

該当なし。本機能はデータベース操作を行わない。

### テーブル別操作詳細

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | DomainError | replace の count < 0 | count に 0 以上の値を指定 |
| - | ArgumentError | strip/lstrip/rstrip に文字列を第2引数として渡した場合 | Char またはChar集合を使用 |
| - | ArgumentError | lpad/rpad のパディング文字の textwidth が 0 | textwidth > 0 の文字を使用 |
| - | ArgumentError | rtruncate/ltruncate/ctruncate の maxwidth < 0 | 0 以上の値を指定 |

### リトライ仕様

該当なし。純粋関数であり、リトライの概念は適用されない。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- `eachsplit` はイテレータベースの遅延評価により、大きな文字列でも全体をメモリに保持せずに分割可能
- `String`/`SubString{String}` に対する特殊化メソッドにより、UTF-8 バイト列レベルでの高速な処理を実現（`chomp` の `@assume_effects :removable :foldable` アノテーション等）
- `replace` は `IOBuffer` の `sizehint` を元文字列の 1.2 倍に設定し、メモリ再割り当てを削減
- `SplitIterator` の型パラメータに splitter の型を含めることで、約30%の実行時間削減と MethodInstance の無効化リスク低減を実現

## セキュリティ考慮事項

該当なし。文字列操作のみであり、セキュリティ上の特別な考慮事項はない。

## 備考

- `split` は `eachsplit` + `collect` として実装されており、メモリ効率が重要な場合は `eachsplit` の直接利用が推奨される
- `rsplit` は `eachrsplit` + `collect` + `reverse!` として実装されている
- `Base.Chars` 型エイリアス（`Union{AbstractChar,Tuple{Vararg{AbstractChar}},AbstractVector{<:AbstractChar},AbstractSet{<:AbstractChar}}`）により、strip系関数で柔軟な文字指定が可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、分割イテレータの内部構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | util.jl | `base/strings/util.jl` | SplitIterator 構造体（753-758行目）: str, splitter, limit, keepempty フィールド |
| 1-2 | util.jl | `base/strings/util.jl` | RSplitIterator 構造体（849-854行目）: 右からの分割用イテレータ |
| 1-3 | util.jl | `base/strings/util.jl` | Chars 型エイリアス（12行目）: strip 系で使用する文字集合の型 |

**読解のコツ**: Julia の型パラメータ `{S<:AbstractString, F}` により splitter の型が SplitIterator にコンパイル時に特殊化される点に注目。これがパフォーマンス向上の鍵である。

#### Step 2: エントリーポイントを理解する

処理の起点となる公開関数を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | util.jl | `base/strings/util.jl` | split 関数（935-938行目）: eachsplit + collect の構成 |
| 2-2 | util.jl | `base/strings/util.jl` | eachsplit 関数群（797-809行目）: 各型に対するディスパッチ |
| 2-3 | util.jl | `base/strings/util.jl` | strip / lstrip / rstrip（406-479行目）: トリミング関数群 |
| 2-4 | util.jl | `base/strings/util.jl` | replace 関数（1156-1160行目）: 置換のエントリーポイント |

**主要処理フロー**:
1. **935行目**: `split` は `eachsplit` の結果を `collect` して配列を返す
2. **797行目**: `eachsplit` は `SplitIterator` を構築して返す
3. **804行目**: `AbstractChar` の区切り文字は `isequal` でラップされる
4. **808行目**: dlm 省略時は `isspace` が使用され、`keepempty=false` がデフォルト

#### Step 3: 分割イテレータの iterate を理解する

SplitIterator の iterate メソッドが分割の核心ロジックである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | util.jl | `base/strings/util.jl` | SplitIterator の iterate（768-784行目）: findnext による区切り検索と SubString 切り出し |
| 3-2 | util.jl | `base/strings/util.jl` | RSplitIterator の iterate（875-904行目）: findprev による右からの検索 |

**主要処理フロー**:
- **770行目**: `findnext(iter.splitter, iter.str, k)` で次の区切り位置を検索
- **775行目**: `SubString(iter.str, i, prevind(iter.str, j))` で部分文字列を切り出し
- **776行目**: `keepempty` フラグに基づき空文字列のフィルタリング

#### Step 4: replace の内部実装を理解する

replace は複数パターン対応の複雑な実装を持つ。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | util.jl | `base/strings/util.jl` | _replace_init（1008-1023行目）: パターン初期化と最初の検索 |
| 4-2 | util.jl | `base/strings/util.jl` | _replace_finish（1026-1039行目）: 逐次置換ループ |
| 4-3 | util.jl | `base/strings/util.jl` | _replace_once（1041-1077行目）: 1回の置換操作（argmin で最左マッチ選択） |

**主要処理フロー**:
- **1012行目**: 各パターンを `_pat_replacer` で関数にラップ
- **1044行目**: `argmin(map(first, rs))` で最左マッチのパターンを選択
- **1050行目**: `unsafe_write` で保持部分をコピー、`_replace` で置換文字列を書き込み

### プログラム呼び出し階層図

```
split(str, splitter)
    |
    +-- eachsplit(str, splitter)
    |       |
    |       +-- SplitIterator(str, splitter, limit, keepempty)
    |       |       |
    |       |       +-- iterate(::SplitIterator)
    |       |               |
    |       |               +-- findnext(splitter, str, k)
    |       |               +-- SubString(str, i, j)
    |       |
    |       +-- [AbstractChar] isequal(splitter)
    |       +-- [Tuple/Vector/Set] in(splitter)
    |
    +-- collect(::SplitIterator) -> Vector{SubString}

rsplit(str, splitter)
    |
    +-- eachrsplit(str, splitter)
    |       +-- RSplitIterator(str, splitter, limit, keepempty)
    |               +-- iterate(::RSplitIterator)
    |                       +-- findprev(splitter, str, to)
    |
    +-- collect -> reverse!

replace(str, pat=>repl)
    |
    +-- _replace_(str, pat_repl, count)
            |
            +-- _replace_init(str, pat_repl, count)
            |       +-- _pat_replacer(pat)
            |       +-- findnext(pat, str, a)
            |
            +-- _replace_finish(io, str, count, ...)
                    +-- _replace_once(io, str, ...)
                            +-- argmin(map(first, rs))
                            +-- _replace(io, repl, str, r, pat)

strip(str) = lstrip(rstrip(str))
    |
    +-- rstrip(isspace, str)
    |       +-- Iterators.reverse(pairs(s))
    |
    +-- lstrip(isspace, str)
            +-- pairs(s)
```

### データフロー図

```
[入力]                       [処理]                           [出力]

str: AbstractString -------> split/eachsplit -------> Vector{SubString} / SplitIterator
  + dlm: splitter               |
  + limit: Integer               +-> findnext(dlm, str, pos)
  + keepempty: Bool              +-> SubString(str, start, end)

str: AbstractString -------> strip/lstrip/rstrip -------> SubString
  + pred: Function               |
  + chars: Chars                 +-> pairs(str) / reverse(pairs(str))

str: AbstractString -------> replace -------> String / IO
  + pat=>repl: Pair...           |
  + count: Integer               +-> _replace_init -> _replace_finish
                                 +-> IOBuffer -> takestring!

str: AbstractString -------> chomp/chop -------> SubString
                                 |
                                 +-> 末尾改行/指定文字数の除去
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| util.jl | `base/strings/util.jl` | ソース | 分割・結合・トリミング・置換の主要実装 |
| search.jl | `base/strings/search.jl` | ソース | findnext/findprev 等の検索関数（split が依存） |
| substring.jl | `base/strings/substring.jl` | ソース | SubString 型の定義（split の戻り値型） |
| basic.jl | `base/strings/basic.jl` | ソース | AbstractString の基本操作（iterate, ncodeunits 等） |
| io.jl | `base/strings/io.jl` | ソース | join 関数の定義 |
| regex.jl | `base/regex.jl` | ソース | Regex 型（split の区切り文字として使用可能） |
| pcre.jl | `base/pcre.jl` | ソース | PCRE2 ライブラリのラッパー（正規表現分割時に使用） |
| iobuffer.jl | `base/iobuffer.jl` | ソース | IOBuffer（replace の内部バッファとして使用） |
