Python組み込み型の勉強：文字列（str）


## メソッド一覧
```python
# In
[v for v in dir('') if not v.startswith('_')]

# Out
['capitalize',
 'casefold',
 'center',
 'count',
 'encode',
 'endswith',
 'expandtabs',
 'find',
 'format',
 'format_map',
 'index',
 'isalnum',
 'isalpha',
 'isascii',
 'isdecimal',
 'isdigit',
 'isidentifier',
 'islower',
 'isnumeric',
 'isprintable',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'ljust',
 'lower',
 'lstrip',
 'maketrans',
 'partition',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'split',
 'splitlines',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'zfill']
```

## 各メソッドの挙動
### capitalize
先頭の単語の最初の文字だけを大文字にし、残りを小文字にする。

```python
# In
'foo Bar BAZ'.capitalize()

# Out
'Foo bar baz'
```

### casefold
特殊な文字(合字等)も含めて可能な限り小文字に変換する。

```python
# In
'A Ｂ ß'.casefold()  # 半角A, 全角B, エスツェット(合字)

# Out
'a ｂ ss'
```

`lower`とは結果が異なる場合がある。

```python
# In
'A Ｂ ß'.lower()

# Out
'a ｂ ß'
```

### center
指定桁数になる様に中央揃えする。

```python
# In
[s.center(10, '_') for s in ['a', 'bb', 'ccc']]

# Out
['____a_____', '____bb____', '___ccc____']
```

パディング文字が省略された場合は半角スペースでパディングされる。

```python
# In
[s.center(10) for s in ['a', 'bb', 'ccc']]

# Out
['    a     ', '    bb    ', '   ccc    ']
```

### count
指定された**文字列**の出現回数を教えてくれる。

```python
# In
'aaaaaaaaaa'.count('aa')

# Out
5
```

カウントを始める開始位置と終了位置を指定できる。

```python
# In
'aaaaaaaaaa'.count('aa', 4)

# Out
3

# In
'aaaaaaaaaa'.count('aa', 4, 6)

# Out
1
```

### encode
文字列(str)をバイト列(bytes)に変換する。

```python
# In
'こんにちは、世界'.encode()

# Out
b'\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf\xe3\x80\x81\xe4\xb8\x96\xe7\x95\x8c'
```

文字コードの変換ができる。

```python
# In
'こんにちは、世界'.encode('sjis')

# Out
b'\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x81A\x90\xa2\x8aE'
```

文字コードを指定する場合、第二引数に`replace`か`ignore`を指定することで変換できない文字列を置換、あるいは無視できる。

この特性を利用することで**特定の文字コードで使用できない文字を除去する**ことができる。

入力がUTF-8なのにDBがSJISなシステムの開発をするときに役立った。

```python
# In
'あ俠い'.encode('sjis', 'replace').decode('sjis')

# Out
'あ?い'  # ?で置換される
```

```python
# In
'あ俠い'.encode('sjis', 'ignore').decode('sjis')

# Out
'あい'  # 除去される
```

第二引数を指定しないと`UnicodeEncodeError`例外が起きる。

```python
# In
try:
    'あい俠うえ'.encode('sjis')
except UnicodeEncodeError as e:
    print(f'{e.start+1}文字目に{e.encoding}で使えない文字が含まれています')

# Out
3文字目にshift_jisで使えない文字が含まれています
```

### endswith
指定された文字列で終わっていればTrueを、そうでなければFalseを返す。

```python
# In
'aabbcc'.endswith('cc')

# Out
True
```

タプルを渡せる(リストは使えない)。

```python
# In
'aabbcc'.endswith(('bb', 'cc'))

# Out
True
```

開始位置と終了位置(省略可)を指定できる。

```python
# In
'aabbcc'.endswith('bb', 2, 4)

# Out
True
```

### expandtabs
タブ文字を8桁の半角スペースに置換する。

```python
# In
'a\tb\tc'.expandtabs()

# Out
'a       b       c'
```

任意の桁数を指定できる。

```python
# In
'a\tb\tc'.expandtabs(4)

# Out
'a   b   c'
```

### find
指定された文字列の最初の出現位置を教えてくれる。見つからなかった場合は`-1`を返す。

```python
# In
'aabbcc'.find('bb')

# Out
2
```

探索開始位置と終了位置(省略可)を指定できる。

```python
# In
'aabbcc'.find('bb', 0, 2)

# Out
-1
```

単に文字列が含まれているか知りたいのなら`in`を使うべき by 公式ドキュメント

```python
# Bad
if 'aabbcc'.find('bb') != -1:
    print('do something')

# Good
if 'bb' in 'aabbcc': 
    print('do something')
```

### format
文字列の書式化を行う。

```python
# In
name = '佐藤'
age = 18
'私の名前は{0}です、年齢は{1}歳です'.format(name, age)

# Out
'私の名前は佐藤です、年齢は18歳です'
```

Python3.6以降なら`f-string`を使うと直感的に記述できる。

```python
# In
name = '佐藤'
age = 18
f'私の名前は{name}です、年齢は{age}歳です'

# Out
'私の名前は佐藤です、年齢は18歳です'
```

パディングしたり演算したりと色々できる。

```python
# In
name = '佐藤'
age = 18
f'私の名前は{name:_^10}です、年齢は{age/1.3:.8}歳です'

# Out
'私の名前は____佐藤____です、年齢は13.846154歳です'
```

### format_map
dict等の辞書オブジェクトを使って文字列を書式化する。

```python
# In
map_ = {'name': '佐藤', 'age': 18}
'私の名前は{name}です、年齢は{age}歳です'.format_map(map_)

# Out
'私の名前は佐藤です、年齢は18歳です'
```

カスタマイズした辞書ライクなオブジェクトを渡すと便利かもしれない。

```python
# In
class MyMap(dict):
    PARAMS = {
        'name': '名前',
        'age': '年齢',
        'like': '趣味',
    }

    def __getitem__(self, key):
        if key not in self.PARAMS:
            return '[不明なパラメータ]'
        
        if key not in self:
            return f'[{self.PARAMS[key]}を入力してください]'
        
        return super().__getitem__(key)


mymap = MyMap()
mymap['name'] = '佐藤'
mymap['age'] = 18

'私の名前は{name}です、年齢は{age}歳です、趣味は{like}です、出身地は{born}です'.format_map(mymap)

# Out
'私の名前は佐藤です、年齢は18歳です、趣味は[趣味を入力してください]です、出身地は[不明なパラメータ]です'
```

### index
基本は`find`と同じで指定された文字列の最初の出現位置を教えてくれる。

```python
# In
'aabbcc'.index('bb')

# Out
2
```

見つからなかった場合に`ValueError`例外が起きるのが`find`と異なる。

```python
# Int
'aabbcc'.index('zz')

# Out
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-170-7650e5585328> in <module>
----> 1 'aabbcc'.index('zz')

ValueError: substring not found
```

### isalnum
長さが1以上、かつ英数字のみで構成されているならTrueを、そうでなければFalseを返す。

```python
# In
''.isalnum()

# Out
False
```

```python
# In
'aA1'.isalnum()

# Out
True
```

```python
# In
' a1 '.isalnum()

# Out
False
```

ただし、日本語を含む全角文字もTrueとして判断されてしまうので実際は正規表現を使ったほうが良い。

```python
# In
'あいうえお'.isalnum()

# Out
True
```

### isalpha
`isalnum`から数字部分を取り除いたと思えばOK。

```python
# In
'aA1'.isalpha()

# Out
False
```

```python
# In
'aA'.isalpha()

# Out
True
```

```python
# In
'あいうえお'.isalpha()

# Out
True
```

### isdecimal, isdigit, isnumeric
長さが1以上、かつ数字のみならTrueを、それ以外ならFalseを返すメソッド達。

ただし、半角数字以外にもTrueを返す場合があるので注意が必要。

```python
def f(n):
    print([n.isdecimal(), n.isdigit(), n.isnumeric()])


# In
f('1')  # 半角

# Out
[True, True, True]

# In
f('１')  # 全角

# Out
[True, True, True]

# In
f('①')  # 丸数字

# Out
[False, True, True]

# In
f('ⅰ')  # アラビア数字

# Out
[False, False, True]

# In
f('一')  # 漢数字

# Out
[False, False, True]
```

### isidentifier
Pythonの変数名として使えるならTrue、そうでなければFalseを返す。

[正確な定義は公式ドキュメントを参照。](https://docs.python.jp/3/reference/lexical_analysis.html#identifiers)

```python
# In
'a'.isidentifier()

# Out
True

# In
'髙'.isidentifier()

# Out
True
```

実際、日本語を変数名として使える。

```python
# In
髙 = 100
髙 / 2

# Out
50.0
```

### islower
`大文字小文字の区別が出来る文字`が1文字以上含まれ、それら全てが小文字ならばTrueを、そうでなければFalseを返す。

空白文字や日本語は区別が無いので無視されるが、区別が無い文字だけの場合はFalseが返る。

```python
# In
'abcあいう \n\t'.islower()

# Out
True

# In
'あいう \n\t'.islower()

# Out
False
```

### isprintable
文字列が空か、その全てが印字可能ならTrueを、それ以外ならFalseを返す・・・とのこと。

```python
# In
'a'.isprintable()

# Out
True
```

改行文字は印字不可能らしい。

```python
# In
'\n'.isprintable()

# Out
False
```

### isspace
長さが1以上、かつ全て空白文字(タブや改行も含む)で構成されているならTrueを、そうでなければFalseを返す。

```python
# In
' \t \n \r\n'.isspace()

# Out
True
```

### istitle
`The End`の様に、先頭のみ大文字の単語の組み合わせならTrueを、それ以外ならFalseを返す。

```python
# In
'Gone With The Wind'.istitle()

# Out
True
```

日本語と英字を組み合わせた場合、日本語部分は無視される。

```python
# In
'風と共に去りぬ - Gone With The Wind -'.istitle()

# Out
True
```

日本語のみの場合はFalseが返る。

```python
# In
'風と共に去りぬ'.istitle()

# Out
False
```

### isupper
`大文字小文字の区別が出来る文字`が1文字以上含まれ、それら全てが大文字ならばTrueを、そうでなければFalseを返す。

空白文字や日本語は区別が無いので無視されるが、区別が無い文字だけの場合はFalseが返る。

```python
# In
'ABCあいう \n\t'.isupper()

# Out
True

# In
'あいう \n\t'.isupper()

# Out
False
```

### join
リスト等のiterableなオブジェクトの全要素を指定された**文字列**で連結する。

```python
# In
','.join(['foo', 'bar', 'baz'])

# Out
'foo,bar,baz'

# In
' >>> '.join(['foo', 'bar', 'baz'])

# Out
'foo >>> bar >>> baz'
```

連結文字は省略可能。

```python
# In
''.join(['foo', 'bar', 'baz'])

# Out
'foobarbaz'
```

文字列もiterableなのでjoinできる。

```python
# In
'-'.join('foobarbaz')

# Out
'f-o-o-b-a-r-b-a-z'
```

### ljust
指定桁数になる様にパディング文字で左詰めする。パディング文字が省略された場合は半角スペースでパディングされる。

```python
# In
'foo'.ljust(10, '_')

# Out
'foo_______'

# In
'foo'.ljust(10)

# Out
'foo       '
```

### lower
全ての大文字を小文字に変換する。全角大文字の場合は全角小文字に変換される。

```python
# In
'A Ａ'.lower()  # 半角, 全角

# Out
'a ａ'  # 半角, 全角
```

### lstrip
先頭にある空白文字(全角スペースや改行等を含む)を全て削除する。

```python
# In
' \u3000\t\n\r\nfoo \u3000\t\n\r\n'.lstrip()

# Out
'foo \u3000\t\n\r\n'
```

削除する文字を指定する事もできる。指定されていない文字にぶつかった時点で処理がストップする。

```python
# In
' <#!# foo ##> '.lstrip(' <#')

# Out
'!# foo ##> '  # !以降は除去されない
```

### str.maketrans, translate
変換テーブルを使った文字置換を行う。

以下は`0〜4`の数字を`*`に置換し、`5〜9`は削除する例。

```python
# In
# 変換テーブルとなる辞書を作成
mapping = dict.fromkeys('01234', '*')
mapping.update(dict.fromkeys('56789', None))  # 削除したい場合はNoneを指定
print('mapping:', mapping)

# translateが扱える形式に変換
table = str.maketrans(mapping)
print('table:', table)
print('type(table):', type(table))

# 変換する
print('\ntranslated: a1b2c3d4e5f6g7h8i9'.translate(table))

# Out
mapping: {'0': '*', '1': '*', '2': '*', '3': '*', '4': '*', '5': None, '6': None, '7': None, '8': None, '9': None}
table: {48: '*', 49: '*', 50: '*', 51: '*', 52: '*', 53: None, 54: None, 55: None, 56: None, 57: None}
type(table): <class 'dict'>

translated: a*b*c*d*efghi
```

辞書を使わない変換も可能。以下の例では`h->H` `l->L` `o->O` `,!は削除`という変換が行われる。

```python
# In
table = str.maketrans('hlo', 'HLO', ',!')
'hello, world!'.translate(table)

# Out
'HeLLO wOrLd'
```

### partition
指定文字列が`最初`に見つかった位置で文字列を分割する。結果は要素数3のタプルとして返ってくる。

```python
# In
'foo//bar//baz'.partition('//')

# Out
('foo', '//', 'bar//baz')
```

分割文字列が含まれない場合は先頭以外空のタプルが返ってくる。

```python
# In
'foo//bar'.partition('+')

# Out
('foo//bar', '', '')
```

### replace
文字列に含まれる特定文字を指定文字に置換する。

```python
# In
'hello, world!'.replace('l', '*')

# Out
'he**o, wor*d!'
```

置換する回数を指定できる。

```python
# In
'hello, world!'.replace('l', '*', 1)

# Out
'he*lo, world!'
```

文字列の置換は出来るが、1文字としてまとめられてしまう。

```python
# In
'hello, world!'.replace('lo', '*')

# Out
'hel*, world!'  # he***, w*r*d! とはならない
```

### rfind
指定された文字列の`最後`の出現位置を教えてくれる(`find`は最初の位置)。見つからなかった場合は`-1`を返す。

```python
# In
'aabbccaa'.rfind('aa')

# Out
6
```

findと同様に探索位置を指定できる。

```python
# In
'aabbccaa'.rfind('aa', 0, 4)

# Out
0
```

### rindex
基本は`rfind`と同じで指定された文字列の最後の出現位置を教えてくれる。

```python
# In
'aabbccaa'.rindex('aa')

# Out
6
```

見つからなかった場合に`ValueError`例外が起きるのが`find`と異なる。

```python
# Int
'aabbccaa'.rindex('zz')

# Out
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-401-8aa97a6819ae> in <module>
----> 1 'aabbccaa'.rindex('zz')

ValueError: substring not found
```

### rjust
指定桁数になる様にパディング文字で右詰めする。パディング文字が省略された場合は半角スペースでパディングされる。

```python
# In
'foo'.rjust(10, '_')

# Out
'_______foo'

# In
'foo'.rjust(10)

# Out
'       foo'
```

### rpartition
指定文字列が`最後`に見つかった位置で文字列を分割する(`partition`は最初の位置で分割)。結果は要素数3のタプルとして返ってくる。

```python
# In
'foo//bar//baz'.rpartition('//')

# Out
('foo//bar', '//', 'baz')
```

分割文字列が含まれない場合は最後以外空のタプルが返ってくる。

```python
# In
'foo//bar'.partition('+')

# Out
('', '', 'foo//bar')
```

### rsplit
空白文字(全角スペースや改行等を含む)、あるいは指定された**文字列**で分割する。

```python
# In
'a b\u3000c\td\ne'.rsplit()

# Out
['a', 'b', 'c', 'd', 'e']
```

連続している場合も正しく分割してくれる。

```python
# In
'a  b\t\tc'.rsplit()

# Out
['a', 'b', 'c']
```

分割文字を指定した場合は**文字列**として一致した場合のみ分割する。分割文字列が2回以上連続する場合は`空白`として分割される。

```python
# In
'foo//bar/baz//////qux'.rsplit('//')

# Out
['foo', 'bar/baz', '', '', 'qux']
```

分割する回数を指定できる。`rsplit`の場合は末尾から分割される。

```python
# In
'foo/bar/baz'.rsplit('/', 1)

# Out
['foo/bar', 'baz']
```

### rstrip
末尾にある空白文字(全角スペースや改行等を含む)を全て削除する。

```python
# In
' \u3000\t\n\r\nfoo \u3000\t\n\r\n'.rstrip()

# Out
' \u3000\t\n\r\nfoo'
```

削除する文字を指定する事もできる。指定されていない文字にぶつかった時点で処理がストップする。

```python
# In
' <## foo ##> ! ##'.rstrip(' >#')

# Out
' <## foo ##> !'  # !以前は除去されない
```

### split
基本は`rsplit`と同じで、空白文字で文字列を分割する。

```python
# In
'a b\u3000c\td\ne'.split()

# Out
['a', 'b', 'c', 'd', 'e']
```

分割回数を指定した場合に`先頭から`分割される点が異なる。

```python
# In
'foo/bar/baz'.split('/', 1)

# Out
['foo', 'bar/baz']
```

### splitlines
改行文字(`\n, \r, \r\n`で分割する。

```python
# In
'line1\nline2\rline3\r\n'.splitlines()

# Out
['line1', 'line2', 'line3']
```

第二引数に1以上の整数かTrueを渡すと改行文字を維持してくれる。

```python
# In
'line1\nline2\rline3\r\n'.splitlines(True)

# Out
['line1\n', 'line2\r', 'line3\r\n']
```

### startswith
指定された文字列で終わっていればTrueを、そうでなければFalseを返す。

```python
# In
'aabbcc'.startswith('aa')

# Out
True
```

タプルを渡せる(リストは使えない)。

```python
# In
'aabbcc'.startswith(('aa', 'bb'))

# Out
True
```

開始位置と終了位置(省略可)を指定できる。

```python
# In
'aabbcc'.startswith('bb', 2, 4)

# Out
True
```

### strip
前後にある空白文字(全角スペースや改行等を含む)を全て削除する。

```python
# In
' \u3000\t\n\r\nfoo \u3000\t\n\r\n'.strip()

# Out
'foo'
```

削除する文字を指定する事もできる。指定されていない文字にぶつかった時点で処理がストップする。

```python
# In
'<## ! ## foo ## ! ##>'.strip(' <>#')

# Out
'! ## foo ## !'  # !より内側は除去されない
```

### swapcase
大文字は小文字に、小文字は大文字に変換する。

```python
# In
'foo Bar BAZ'.swapcase()

# Out
'FOO bAR baz'
```

### title
`The End`の様に、各単語の先頭のみ大文字に、それ以外を小文字にする。

```python
# In
'foo Bar BAZ'.title()

# Out
'Foo Bar Baz'
```

### upper
全ての小文字を大文字に変換する。全角小文字の場合は全角大文字に変換される。

```python
# In
'a ａ'.upper()  # 半角, 全角

# Out
'A Ａ'  # 半角, 全角
```

### zfill
指定された長さになる様に`0`で右詰めする。

```python
# In
'100'.zfill(9)

# Out
'000000100'
```

先頭が`+`か`-`の符号で始まる場合、符号込みで指定された長さにする。

```python
# In
'-100'.zfill(9)

# Out
'-00000100'  # 0は5個
```

## 特定の文字列が含まれるかチェック
`in`で簡単にチェックできる。

```python
# In
'bar' in 'foo bar baz'

# Out
True
```

## 長い文字列の宣言
### バックスラッシュで連結

```python
s1 = 'ちょっと長い、\
けっこう長い、\
かなり長い、\
文字列の宣言'
```

### トリプルクオート（ヒアドキュメント）
`'''`もしくは`"""　`を使えばバックスラッシュ無しで記述できる。改行も含まれてしまう点に注意。

```python
s2 = '''ちょっと長い、
けっこう長い、
かなり長い、
文字列の宣言'''
```

### 半角スペースで連結
```python
s3 = 'ちょっと長い、' 'けっこう長い、' 'かなり長い、' '文字列の宣言'
```

### 括弧`()``[]``{}`
インデント出来るので可読性が高い。

```python
s4 = (
    'ちょっと長い、'  # カンマをつけるとtupleになってしまうので注意
    'けっこう長い、'
    'かなり長い、'
    '文字列の宣言'
)
s5 = [
    'ちょっと長い、'
    'けっこう長い、'
    'かなり長い、'
    '文字列の宣言'
].pop()
s6 = {
    'msg': 'ちょっと長い、'
           'けっこう長い、'
           'かなり長い、'
           '文字列の宣言'
}['msg']
```

関数にも渡しやすいので個人的には括弧を多用する。

```python
# In
def replace(s, old, new):
    return s.replace(old, new)

    
replace('hello, '
        'world!',
        'lo',
        '*')

# Out
'hel*, world!'
```

### 任意回数繰り返す
テストを書く際に多用する。

```python
# In
'foo' * 10

# Out
'foofoofoofoofoofoofoofoofoofoo'
```

## シーケンス操作
strはiterableなので`list`と同様のシーケンス操作が可能。

### ループ処理
```python
# In
for i, v in enumerate('abc'):
    print(i, v)

# Out
0 a
1 b
2 c
```

### 長さの取得
```python
# In
len('abc')

# Out
3
```

### 展開(アンパック)
```python
# In
a, b, *c = 'abcdefg'
print(a, b, c)

# Out
a b ['c', 'd', 'e', 'f', 'g']
```

### スライス
インデックス指定によるスライス操作が可能。

#### 先頭の文字を取得

```python
'abcd'[0]  # 'a'
```

#### 最後の文字を取得
```python
'abcd'[-1]  # 'd'
```

#### n文字目以降を取得
```python
'abcd'[2:]  # 'cd'
```

#### n文字目までを取得
```python
'abcd'[:2]  # 'ab'
```

#### n~m文字目を取得
```python
'abcd'[1:3]  # 'bc'
```

####  n文字飛ばしで取得
```python
'abcd'[::2]  # 'ac'

'abcdefgh'[::4]  # 'ae'
```

#### 反転する
正確には**末尾から一文字ずつ取得**させている。

```python
'abcd'[::-1]  # 'dcba'
```

### iterableを処理する関数に渡す
iterableなオブジェクトを引数に取る関数に渡せる。

```python
 # In
dict(zip('abc', [1,2,3]))

# Out
{'a': 1, 'b': 2, 'c': 3}
```