[iOS] iOS10.3 で、ファイル名に濁点・半濁点のついたファイルにアクセスできない(Unicode 正規化)
- 追記(2017/04/09 23:50) Windows が採用しているファイルパスの正規化形式「NFC」で作成されたファイルにアクセス出来ないことが問題なのであり、「iTunes for Windows」の問題ではないと考えられる。そこでタイトルを以下のように変更。
- 旧:[iOS] iOS10.3 で、iTunes for Windows から転送した濁点付きファイルにアクセスできない(Unicode 正規化)
- 新:[iOS] iOS10.3 で、ファイル名に濁点・半濁点のついたファイルにアクセスできない(Unicode 正規化)
はじめに
- iOS10.3上のアプリが、Windows などで作成した濁点・半濁点付きのファイルにアクセスできない問題について、解決方法を書いたものである。
- コメント、ツッコミ歓迎です。
問題
- Windows 上で作成したファイルを、iTunes のファイル共有機能などを通じてアプリに転送した際、濁点・半濁点を含むファイルにアプリ上からアクセスできない
- 環境
- Windows 上で作成したファイルを、iTunes のファイル共有機能などを通じてアプリに転送した際、濁点・半濁点を含むファイルにアプリ上からアクセスできない
- 環境
OS/App | Version |
---|---|
iOS | 10.3.1 |
Windows | 7 |
iTunes | 12.6 |
Xcode | 8.3 |
- 例:2つのファイルにアクセス
- ぱ_Mac.pdf:macOS(Sierra) の iTunes から転送
- ぱ_Win.pdf:Windows7 の iTunes から転送
NSArray* filepaths = ...
NSFileManager *fileManager = [NSFileManager defaultManager];
for(NSString *filepath in filepaths) {
NSLog(@"==== %@ ====", filepath.lastPathComponent);
NSLog(@"NSFileManager\t=> %@", ([fileManager fileExistsAtPath:filepath] ? @"YES":@"NO"));
}
==== ぱ_Mac.pdf ====
NSFileManager => YES
==== ぱ_Win.pdf ====
NSFileManager => YES
==== ぱ_Mac.pdf ====
NSFileManager => YES
==== ぱ_Win.pdf ====
NSFileManager => NO <<<< ??
原因
- 実際のファイル名と、アクセスする際のファイル名について、それぞれの Unicode の正規化形式が異なり、別ファイル扱いとなってしまうため
アクセス方法 | 正規化形式 | 備考 |
---|---|---|
Windows で作成したファイル | NFC | macOS で作成したファイルは NFD |
NSFileManagerでアクセス | NFD | iOS10.3 で採用されたファイルシステム Apple File System(APFS) は、 NFD(Normalization Form Canonical Decomposition) 形式を前提としており、NSFileManager が内部でNFD形式に変換してるものと考えられる |
解決方法
Foundation API を使う場合
- [[NSURL alloc] initFileURLWithFileSystemRepresentation:[filepath UTF8String] isDirectory:NO relativeToURL:nil] を使うようにする
for(NSString* filepath in filepaths) {
NSLog(@"==== %@ ====", filepath.lastPathComponent);
NSURL* fileURL = [[NSURL alloc] initFileURLWithFileSystemRepresentation:[filepath UTF8String] isDirectory:NO relativeToURL:nil];
NSLog(@"exist(NSURL)\t=> %@", ([fileURL checkResourceIsReachableAndReturnError:nil] ? @"YES":@"NO"));
}
出力結果(iOS10.0.1)
==== ぱ_Mac.pdf ====
NSURL => YES
==== ぱ_Win.pdf ====
NSURL => YES
出力結果(iOS10.3)
==== ぱ_Mac.pdf ====
NSURL => YES
==== ぱ_Win.pdf ====
NSURL => YES
fopen などの低レベルな関数を使う場合
- NSURL.fileSystemRepresentation か、 NSString.UTF8String を使う
fopen
...
for(NSString* filepath in filepaths) {
NSLog(@"==== %@ ====", filepath.lastPathComponent);
// NSURLでファイルパス情報を取得できたと仮定
NSURL* fileURL = [[NSURL alloc] initFileURLWithFileSystemRepresentation:[filepath UTF8String] isDirectory:NO relativeToURL:nil];
NSLog(@"fopen(NSRUL.fileSystemRepresentation)\t=> %@", ((fopen([fileURL fileSystemRepresentation], "r") != NULL) ? @"YES":@"NO"));
NSLog(@"fopen(NSString.UTF8String)\t\t\t\t=> %@", ((fopen([filepath UTF8String], "r") != NULL) ? @"YES":@"NO"));
}
出力結果(iOS10.0.1)
==== ぱ_Mac.pdf ====
fopen(NSURL.fileSystemRepresentation) => YES
fopen(NSString.UTF8String) => YES
==== ぱ_Win.pdf ====
fopen(NSURL.fileSystemRepresentation) => YES
fopen(NSString.UTF8String) => YES
出力結果(iOS10.3)
==== ぱ_Mac.pdf ====
fopen(NSURL.fileSystemRepresentation) => YES
fopen(NSString.UTF8String) => YES
==== ぱ_Win.pdf ====
fopen(NSURL.fileSystemRepresentation) => YES
fopen(NSString.UTF8String) => YES
疑問
NSString.fileSystemRepresentation の使用について
- 低レベルな関数を使ってアクセスする場合、UTF8String ではなく NSString.fileSystemRepresentation を使うべし、という記述を見かける(ここ、ここなど)が、NSString.fileSystemRepresentation だとアクセス出来ない。何かが間違えているのだろうか?
NSString.fileSystemRepresentation
...
for(NSString* filepath in filepaths) {
NSLog(@"==== %@ ====", filepath.lastPathComponent);
NSLog(@"fopen(fileSystemRepresentation)=> %@", ((fopen([filepath fileSystemRepresentation], "r") != NULL) ? @"YES":@"NO"));
}
出力結果
==== ぱ_Mac.pdf ====
fopen(fileSystemRepresentation) => YES
==== ぱ_Win.pdf ====
fopen(fileSystemRepresentation) => NO <<< ??
- 追記(2017/04/09 23:10)
-
Apple File System Guide の FAQによれば、「Use the fileSystemRepresentation property of NSURL objects」とあるので、NString.fileSystemRepresentationではなく、NSURL.fileSystemRepresentation が正しいのではないだろうか。
- 以下、引用(太線は追加)
To avoid introducing bugs in your code with mismatched Unicode normalization in filenames:
Use high-level Foundation APIs such as NSFileManager and NSURL when interacting with the filesystem
Use the fileSystemRepresentation property of NSURL objects when creating and opening files with lower-level filesystem APIs such as POSIX open(2), or when storing filenames externally from the filesystem
おまけ
- Xcode などの標準出力文字列をコピーした際に、正規化形式もそのままコピペできる
NFC/NFD文字列をコピペ
NSString* filename1 = @"ぱ_Win.pdf"; // NFC
NSString* filename2 = @"ぱ_Win.pdf"; // NFD
NSLog(@"isEqual? %d", [filename1 isEqualToString:filename2]);
出力結果
isEqual? 0
参考
iOS10.3 / APFS
- iOS10.3のファイルパス問題について
- iOS10.3 APFSの対処 - Qiita
- macOS上のAPFSはUnicode Normalizationを行うのか?
- Mac OS X の NFD 問題での対策諸々 - Qiita
- Swiftでの文字列比較におけるUnicode正規化を巡る注意点 - Qiita
- SwiftでのUnicode正規化問題 続編:HFS+との整合性 - Qiita
- Apple File System Guide
- 2byte language problem in iOS 10.3 | Apple Developer Forums
- APFS’s “Bag of Bytes” Filenames
- APFS does not normalize Unicode filenames | Hacker News
- HFS+のテキストエンコーディング
Unicode 正規化形式
API
for(NSString* filepath in filepaths) {
NSLog(@"==== %@ ====", filepath.lastPathComponent);
NSURL* fileURL = [[NSURL alloc] initFileURLWithFileSystemRepresentation:[filepath UTF8String] isDirectory:NO relativeToURL:nil];
NSLog(@"exist(NSURL)\t=> %@", ([fileURL checkResourceIsReachableAndReturnError:nil] ? @"YES":@"NO"));
}
==== ぱ_Mac.pdf ====
NSURL => YES
==== ぱ_Win.pdf ====
NSURL => YES
==== ぱ_Mac.pdf ====
NSURL => YES
==== ぱ_Win.pdf ====
NSURL => YES
...
for(NSString* filepath in filepaths) {
NSLog(@"==== %@ ====", filepath.lastPathComponent);
// NSURLでファイルパス情報を取得できたと仮定
NSURL* fileURL = [[NSURL alloc] initFileURLWithFileSystemRepresentation:[filepath UTF8String] isDirectory:NO relativeToURL:nil];
NSLog(@"fopen(NSRUL.fileSystemRepresentation)\t=> %@", ((fopen([fileURL fileSystemRepresentation], "r") != NULL) ? @"YES":@"NO"));
NSLog(@"fopen(NSString.UTF8String)\t\t\t\t=> %@", ((fopen([filepath UTF8String], "r") != NULL) ? @"YES":@"NO"));
}
==== ぱ_Mac.pdf ====
fopen(NSURL.fileSystemRepresentation) => YES
fopen(NSString.UTF8String) => YES
==== ぱ_Win.pdf ====
fopen(NSURL.fileSystemRepresentation) => YES
fopen(NSString.UTF8String) => YES
==== ぱ_Mac.pdf ====
fopen(NSURL.fileSystemRepresentation) => YES
fopen(NSString.UTF8String) => YES
==== ぱ_Win.pdf ====
fopen(NSURL.fileSystemRepresentation) => YES
fopen(NSString.UTF8String) => YES
NSString.fileSystemRepresentation の使用について
- 低レベルな関数を使ってアクセスする場合、UTF8String ではなく NSString.fileSystemRepresentation を使うべし、という記述を見かける(ここ、ここなど)が、NSString.fileSystemRepresentation だとアクセス出来ない。何かが間違えているのだろうか?
...
for(NSString* filepath in filepaths) {
NSLog(@"==== %@ ====", filepath.lastPathComponent);
NSLog(@"fopen(fileSystemRepresentation)=> %@", ((fopen([filepath fileSystemRepresentation], "r") != NULL) ? @"YES":@"NO"));
}
==== ぱ_Mac.pdf ====
fopen(fileSystemRepresentation) => YES
==== ぱ_Win.pdf ====
fopen(fileSystemRepresentation) => NO <<< ??
- 追記(2017/04/09 23:10)
- Apple File System Guide の FAQによれば、「Use the fileSystemRepresentation property of NSURL objects」とあるので、NString.fileSystemRepresentationではなく、NSURL.fileSystemRepresentation が正しいのではないだろうか。
- 以下、引用(太線は追加)
To avoid introducing bugs in your code with mismatched Unicode normalization in filenames:
Use high-level Foundation APIs such as NSFileManager and NSURL when interacting with the filesystem
Use the fileSystemRepresentation property of NSURL objects when creating and opening files with lower-level filesystem APIs such as POSIX open(2), or when storing filenames externally from the filesystem
おまけ
- Xcode などの標準出力文字列をコピーした際に、正規化形式もそのままコピペできる
NFC/NFD文字列をコピペ
NSString* filename1 = @"ぱ_Win.pdf"; // NFC
NSString* filename2 = @"ぱ_Win.pdf"; // NFD
NSLog(@"isEqual? %d", [filename1 isEqualToString:filename2]);
出力結果
isEqual? 0
参考
iOS10.3 / APFS
- iOS10.3のファイルパス問題について
- iOS10.3 APFSの対処 - Qiita
- macOS上のAPFSはUnicode Normalizationを行うのか?
- Mac OS X の NFD 問題での対策諸々 - Qiita
- Swiftでの文字列比較におけるUnicode正規化を巡る注意点 - Qiita
- SwiftでのUnicode正規化問題 続編:HFS+との整合性 - Qiita
- Apple File System Guide
- 2byte language problem in iOS 10.3 | Apple Developer Forums
- APFS’s “Bag of Bytes” Filenames
- APFS does not normalize Unicode filenames | Hacker News
- HFS+のテキストエンコーディング
Unicode 正規化形式
API
NSString* filename1 = @"ぱ_Win.pdf"; // NFC
NSString* filename2 = @"ぱ_Win.pdf"; // NFD
NSLog(@"isEqual? %d", [filename1 isEqualToString:filename2]);
isEqual? 0
iOS10.3 / APFS
- iOS10.3のファイルパス問題について
- iOS10.3 APFSの対処 - Qiita
- macOS上のAPFSはUnicode Normalizationを行うのか?
- Mac OS X の NFD 問題での対策諸々 - Qiita
- Swiftでの文字列比較におけるUnicode正規化を巡る注意点 - Qiita
- SwiftでのUnicode正規化問題 続編:HFS+との整合性 - Qiita
- Apple File System Guide
- 2byte language problem in iOS 10.3 | Apple Developer Forums
- APFS’s “Bag of Bytes” Filenames
- APFS does not normalize Unicode filenames | Hacker News
- HFS+のテキストエンコーディング
Unicode 正規化形式
API
Author And Source
この問題について([iOS] iOS10.3 で、ファイル名に濁点・半濁点のついたファイルにアクセスできない(Unicode 正規化)), 我々は、より多くの情報をここで見つけました https://qiita.com/plusadd/items/0f5ff5c470797f31a890著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .