Swift Digest

String.Encoding の名前変換

String Encoding Names

Proposal
SF-0033
Authors
YOCKOW
Status
Accepted

このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら

01 何が問題だったのか

文字エンコーディング名は、HTTP ヘッダの Content-Type: text/plain; charset=UTF-8 や XML 宣言の <?xml version="1.0" encoding="Shift_JIS"?> のように、コンピュータネットワーク上で広く使われています。そのため、Swift 上でも String.Encoding と文字エンコーディング名を相互に変換できる必要があります。

しかし、Swift Foundation には標準でその変換 API が用意されていませんでした。Darwin 上であれば CoreFoundation の CFStringConvertEncodingToIANACharSetName などのレガシーな C API を経由することで近いことはできますが、

  • 単純な値を得るために複数の CF 関数を組み合わせる必要があり、Swift らしくありません。
  • CF の API はレガシーで、現代的な要件を必ずしも満たしません。
  • CF の API は非 Darwin プラットフォーム上で Swift から直接呼ばれることを公式には想定していません。

実際、String.Encoding から IANA charset 名を得るだけでも、CFStringConvertNSStringEncodingToEncodingCFStringEncoding に変換し、CFStringConvertEncodingToIANACharSetNameCFString? を取得し、さらに CFStringGetCString を使ってバッファ経由で Swift の String に変換する、といった手間が必要でした。

extension String.Encoding {
  var nameInLegacyWay: String? {
    let cfStrEncValue: CFStringEncoding = CFStringConvertNSStringEncodingToEncoding(self.rawValue)
    let cfStrEncName: CFString? = CFStringConvertEncodingToIANACharSetName(cfStrEncValue)
    // CFString から Swift の String への変換にもさらにバッファ操作が必要
    ...
  }
}

Swift Foundation としてプラットフォームを問わず使える、シンプルで Swift らしい API が求められていました。

02 どのように解決されるのか

String.Encoding に、IANA の “Character Sets” レジストリの名前との相互変換を行う ianaName プロパティと init(ianaName:) イニシャライザが追加されます。FoundationPreview 6.3 以降で利用できます。

extension String.Encoding {
    /// この encoding に対応する IANA レジストリの "charset" 名。
    @available(FoundationPreview 6.3, *)
    public var ianaName: String?

    /// IANA レジストリの "charset" 名からインスタンスを生成する。
    @available(FoundationPreview 6.3, *)
    public init?(ianaName: String)
}

これにより、これまで CoreFoundation の関数を組み合わせて書いていた処理が次のように書けるようになります。

print(String.Encoding.utf8.ianaName!)                       // "UTF-8"
print(String.Encoding(ianaName: "ISO_646.irv:1991") == .ascii) // true

IANA の Character Sets を採用しているのは、W3C が XML や HTTP ヘッダで IANA charset 名を使うことを推奨しており、CF API も関数名が示す通り IANA charset 名を扱っているためです。一方で、CF は charset 名のパース挙動が一貫していないなどの問題があるため、CF とまったく同じ振る舞いを再現するのではなく、シンプルで予測可能な対応関係に絞られています。

String.Encoding に対応する IANA charset 名

String.Encoding の主要なケースと IANA charset 名の対応は次の通りです。.nextstep.nonLossyASCII.symbol のように IANA に対応する名前がないものは、ianaNamenil になります。

String.Encoding IANA “charset” 名
.ascii US-ASCII
.iso2022JP ISO-2022-JP
.isoLatin1 ISO-8859-1
.isoLatin2 ISO-8859-2
.japaneseEUC EUC-JP
.macOSRoman macintosh
.shiftJIS Shift_JIS
.unicode / .utf16 UTF-16
.utf16BigEndian UTF-16BE
.utf16LittleEndian UTF-16LE
.utf32 UTF-32
.utf32BigEndian UTF-32BE
.utf32LittleEndian UTF-32LE
.utf8 UTF-8
.windowsCP1250 windows-1250
.windowsCP1251 windows-1251
.windowsCP1252 windows-1252
.windowsCP1253 windows-1253
.windowsCP1254 windows-1254

名前の生成と解釈

ianaName は、IANA Character Sets で定義されている Preferred MIME Name もしくは Name を返します。CF とは異なり、必要に応じて大文字も使われます。

init(ianaName:) は、与えられた名前を Preferred MIME NameNameAliases と ASCII の大文字小文字を区別せずに比較します。たとえば ISO_646.irv:1991US-ASCII の Alias として登録されているため、String.Encoding(ianaName: "ISO_646.irv:1991").ascii と等しくなります。

CF は ICU を介して UTS#22 の “Charset Alias Matching” 規則の一部を使った緩めのマッチングを行いますが、その振る舞いは予測しづらく、複雑になりがちです。本 API では、Aliases を含めた単純な ASCII の大文字小文字無視比較に絞ることで、挙動を分かりやすく保っています。

03 今後の見通し

Proposal にはいくつかの将来の方向性が示されています。いずれも構想の段階で、実現を約束するものではありません。

Web アプリケーションでの活用

文字エンコーディング名のパース処理が標準で提供されることで、Swift で書かれた Web アプリケーションが独自にパーサを実装する必要がなくなります。また、FoundationInternationalization には ICU API をラップした文字列コンバータがすでに存在し、ICU のコンバータを生成するには IANA charset 名が必要です。ianaName の導入により、現時点では未提供の文字エンコーディング変換を実装しやすくなることが期待されます。

より一般的な string encoder / decoder

より長期的には、汎用的な文字列 encoder / decoder とそれを表すプロトコルの導入が議論されています(Unicode Processing APIs など)。さらに、名前と実装が密に結びついた型(たとえば WHATWG Encoding Standard 用の型)を別途提供する案もあります。たとえば次のような形が想像されています。

public protocol StrawmanStringEncodingProtocol {
    static func encoding(for name: String) -> Self?
    var name: String? { get }
    var encoder: (any StringToByteStreamEncoder)? { get }
    var decoder: (any ByteStreamToUnicodeScalarsDecoder)? { get }
}

public struct IANACharset: StrawmanStringEncodingProtocol { ... }
public struct WHATWGEncoding: StrawmanStringEncodingProtocol { ... }

String.EncodingRawRepresentableRawValueUInt)であり、名前と実装が緩く結びついた汎用的な型です。一方で WHATWG Encoding Standard は名前と実装を密に結びつけており、Web ブラウザと JavaScript API を主眼に置いています。そのため String.Encoding には収まりきらず、別の型として整理する余地があるとされています。