libs.utils.textutil

libs/utils/textutil.py

  1"""
  2libs/utils/textutil.py
  3"""
  4
  5import os
  6from enum import Enum, auto
  7from math import ceil, floor
  8from typing import TYPE_CHECKING, Any
  9
 10import libs.global_value as g
 11
 12if TYPE_CHECKING:
 13    from pathlib import Path
 14
 15
 16class ConversionType(Enum):
 17    """変換タイプ"""
 18
 19    HtoZ = auto()
 20    """半角文字を全角文字に変換(数字のみ)"""
 21    ZtoH = auto()
 22    """全角文字を半角文字に変換(数字のみ)"""
 23    HtoK = auto()
 24    """ひらがなをカタカナに変換"""
 25    KtoH = auto()
 26    """カタカナをひらがなに変換"""
 27
 28
 29def str_conv(text: str, kind: ConversionType) -> str:
 30    """
 31    文字列変換
 32
 33    Args:
 34        text (str): 変換対象文字列
 35        kind (ConversionType): 変換種類
 36
 37    Returns:
 38        str: 変換後の文字列
 39
 40    """
 41    zen = "".join(chr(0xFF10 + i) for i in range(10))
 42    han = "".join(chr(0x30 + i) for i in range(10))
 43    hira = "".join(chr(0x3041 + i) for i in range(86))
 44    kana = "".join(chr(0x30A1 + i) for i in range(86))
 45
 46    match kind:
 47        case ConversionType.HtoZ:
 48            trans_table = str.maketrans(han, zen)
 49        case ConversionType.ZtoH:
 50            trans_table = str.maketrans(zen, han)
 51        case ConversionType.HtoK:
 52            trans_table = str.maketrans(hira, kana)
 53        case ConversionType.KtoH:
 54            trans_table = str.maketrans(kana, hira)
 55        case _:
 56            return text
 57
 58    return text.translate(trans_table)
 59
 60
 61def save_file_path(filename: str, delete: bool = False) -> "Path":
 62    """
 63    保存ファイルのフルパスを取得
 64
 65    Args:
 66        filename (str): デフォルトファイル名
 67        delete (bool, optional): 生成済みファイルを削除. Defaults to False.
 68
 69    Returns:
 70        Path: 保存ファイルパス
 71
 72    """
 73    _, file_ext = os.path.splitext(filename)
 74    file_name = f"{g.params.filename}{file_ext}" if g.params.filename else f"{filename}"
 75    file_path = g.cfg.setting.work_dir / file_name
 76
 77    if file_path.exists() and delete:
 78        os.remove(file_path)
 79
 80    return file_path
 81
 82
 83def split_balanced(data: list[list[Any]], target_size: int, tolerance: float = 0.15) -> list[list[Any]]:
 84    """
 85    リストデータを指定個数で分割
 86
 87    Args:
 88        data (list[list[Any]]): 対象データ
 89        target_size (int): 分割サイズ
 90        tolerance (float, optional): 個数誤差. Defaults to 0.15.
 91
 92    Returns:
 93        list[list[Any]]: 分割したリスト
 94
 95    """
 96    # 分割サイズに0が指定されている場合は何もしない
 97    if not target_size:
 98        return data
 99
100    n = len(data)
101    if n == 0:
102        return []
103
104    min_size = int(target_size * (1 - tolerance))
105    max_size = int(target_size * (1 + tolerance))
106
107    # 最小ブロック数の候補を計算
108    min_blocks = ceil(n / max_size)
109    max_blocks = floor(n / min_size)
110
111    # 許容範囲内でブロック数を決める(なるべく少ない)
112    for num_blocks in range(min_blocks, max_blocks + 1):
113        size = n / num_blocks
114        if min_size <= size <= max_size:
115            break
116    else:
117        # 条件を満たすブロック数がない場合は単純均等割り
118        num_blocks = ceil(n / target_size)
119
120    # 実際の分割処理
121    base_size = n // num_blocks
122    remainder = n % num_blocks
123
124    result: list[list[Any]] = []
125    start = 0
126    for i in range(num_blocks):
127        end = start + base_size + (1 if i < remainder else 0)
128        result.append(data[start:end])
129        start = end
130
131    return result
132
133
134def split_text_blocks(text: str, limit: int = 2000) -> list[str]:
135    """
136    指定文字数でテキストを行単位で分割してリストにする
137
138    Args:
139        text (str): 対象文字列
140        limit (int, optional): 分割文字数. Defaults to 2000.
141
142    Returns:
143        list[str]: 分割リスト
144
145    """
146    blocks: list[str] = []
147    current_data = ""
148    buffer_data = ""
149    in_code = False
150    min_gap_after_code_start = 10
151    lines_count = 0
152
153    for _, line in enumerate(text.splitlines(keepends=True)):
154        stripped = line.strip()
155        buffer_data += line
156
157        # --- コードブロック開始/終了検出 ---
158        if stripped.startswith("```"):
159            in_code = not in_code
160            if not in_code:
161                current_data += buffer_data
162                buffer_data = ""
163            continue
164
165        lines_count += 1 if in_code else 0
166
167        # --- 文字数チェック ---
168        if len(current_data + buffer_data) > limit:
169            if lines_count > min_gap_after_code_start:
170                if in_code:
171                    blocks.append(current_data + buffer_data + "```\n")
172                    buffer_data = "```\n"
173                else:
174                    blocks.append(current_data + buffer_data)
175                    buffer_data = ""
176            else:
177                blocks.append(current_data)  # 先頭の改行は削除されてしまう
178            current_data = ""
179
180    return blocks
class ConversionType(enum.Enum):
17class ConversionType(Enum):
18    """変換タイプ"""
19
20    HtoZ = auto()
21    """半角文字を全角文字に変換(数字のみ)"""
22    ZtoH = auto()
23    """全角文字を半角文字に変換(数字のみ)"""
24    HtoK = auto()
25    """ひらがなをカタカナに変換"""
26    KtoH = auto()
27    """カタカナをひらがなに変換"""

変換タイプ

HtoZ = <ConversionType.HtoZ: 1>

半角文字を全角文字に変換(数字のみ)

ZtoH = <ConversionType.ZtoH: 2>

全角文字を半角文字に変換(数字のみ)

HtoK = <ConversionType.HtoK: 3>

ひらがなをカタカナに変換

KtoH = <ConversionType.KtoH: 4>

カタカナをひらがなに変換

def str_conv(text: str, kind: ConversionType) -> str:
30def str_conv(text: str, kind: ConversionType) -> str:
31    """
32    文字列変換
33
34    Args:
35        text (str): 変換対象文字列
36        kind (ConversionType): 変換種類
37
38    Returns:
39        str: 変換後の文字列
40
41    """
42    zen = "".join(chr(0xFF10 + i) for i in range(10))
43    han = "".join(chr(0x30 + i) for i in range(10))
44    hira = "".join(chr(0x3041 + i) for i in range(86))
45    kana = "".join(chr(0x30A1 + i) for i in range(86))
46
47    match kind:
48        case ConversionType.HtoZ:
49            trans_table = str.maketrans(han, zen)
50        case ConversionType.ZtoH:
51            trans_table = str.maketrans(zen, han)
52        case ConversionType.HtoK:
53            trans_table = str.maketrans(hira, kana)
54        case ConversionType.KtoH:
55            trans_table = str.maketrans(kana, hira)
56        case _:
57            return text
58
59    return text.translate(trans_table)

文字列変換

Arguments:
  • text (str): 変換対象文字列
  • kind (ConversionType): 変換種類
Returns:

str: 変換後の文字列

def save_file_path(filename: str, delete: bool = False) -> pathlib.Path:
62def save_file_path(filename: str, delete: bool = False) -> "Path":
63    """
64    保存ファイルのフルパスを取得
65
66    Args:
67        filename (str): デフォルトファイル名
68        delete (bool, optional): 生成済みファイルを削除. Defaults to False.
69
70    Returns:
71        Path: 保存ファイルパス
72
73    """
74    _, file_ext = os.path.splitext(filename)
75    file_name = f"{g.params.filename}{file_ext}" if g.params.filename else f"{filename}"
76    file_path = g.cfg.setting.work_dir / file_name
77
78    if file_path.exists() and delete:
79        os.remove(file_path)
80
81    return file_path

保存ファイルのフルパスを取得

Arguments:
  • filename (str): デフォルトファイル名
  • delete (bool, optional): 生成済みファイルを削除. Defaults to False.
Returns:

Path: 保存ファイルパス

def split_balanced( data: list[list[typing.Any]], target_size: int, tolerance: float = 0.15) -> list[list[typing.Any]]:
 84def split_balanced(data: list[list[Any]], target_size: int, tolerance: float = 0.15) -> list[list[Any]]:
 85    """
 86    リストデータを指定個数で分割
 87
 88    Args:
 89        data (list[list[Any]]): 対象データ
 90        target_size (int): 分割サイズ
 91        tolerance (float, optional): 個数誤差. Defaults to 0.15.
 92
 93    Returns:
 94        list[list[Any]]: 分割したリスト
 95
 96    """
 97    # 分割サイズに0が指定されている場合は何もしない
 98    if not target_size:
 99        return data
100
101    n = len(data)
102    if n == 0:
103        return []
104
105    min_size = int(target_size * (1 - tolerance))
106    max_size = int(target_size * (1 + tolerance))
107
108    # 最小ブロック数の候補を計算
109    min_blocks = ceil(n / max_size)
110    max_blocks = floor(n / min_size)
111
112    # 許容範囲内でブロック数を決める(なるべく少ない)
113    for num_blocks in range(min_blocks, max_blocks + 1):
114        size = n / num_blocks
115        if min_size <= size <= max_size:
116            break
117    else:
118        # 条件を満たすブロック数がない場合は単純均等割り
119        num_blocks = ceil(n / target_size)
120
121    # 実際の分割処理
122    base_size = n // num_blocks
123    remainder = n % num_blocks
124
125    result: list[list[Any]] = []
126    start = 0
127    for i in range(num_blocks):
128        end = start + base_size + (1 if i < remainder else 0)
129        result.append(data[start:end])
130        start = end
131
132    return result

リストデータを指定個数で分割

Arguments:
  • data (list[list[Any]]): 対象データ
  • target_size (int): 分割サイズ
  • tolerance (float, optional): 個数誤差. Defaults to 0.15.
Returns:

list[list[Any]]: 分割したリスト

def split_text_blocks(text: str, limit: int = 2000) -> list[str]:
135def split_text_blocks(text: str, limit: int = 2000) -> list[str]:
136    """
137    指定文字数でテキストを行単位で分割してリストにする
138
139    Args:
140        text (str): 対象文字列
141        limit (int, optional): 分割文字数. Defaults to 2000.
142
143    Returns:
144        list[str]: 分割リスト
145
146    """
147    blocks: list[str] = []
148    current_data = ""
149    buffer_data = ""
150    in_code = False
151    min_gap_after_code_start = 10
152    lines_count = 0
153
154    for _, line in enumerate(text.splitlines(keepends=True)):
155        stripped = line.strip()
156        buffer_data += line
157
158        # --- コードブロック開始/終了検出 ---
159        if stripped.startswith("```"):
160            in_code = not in_code
161            if not in_code:
162                current_data += buffer_data
163                buffer_data = ""
164            continue
165
166        lines_count += 1 if in_code else 0
167
168        # --- 文字数チェック ---
169        if len(current_data + buffer_data) > limit:
170            if lines_count > min_gap_after_code_start:
171                if in_code:
172                    blocks.append(current_data + buffer_data + "```\n")
173                    buffer_data = "```\n"
174                else:
175                    blocks.append(current_data + buffer_data)
176                    buffer_data = ""
177            else:
178                blocks.append(current_data)  # 先頭の改行は削除されてしまう
179            current_data = ""
180
181    return blocks

指定文字数でテキストを行単位で分割してリストにする

Arguments:
  • text (str): 対象文字列
  • limit (int, optional): 分割文字数. Defaults to 2000.
Returns:

list[str]: 分割リスト