Posted on 2015年7月7日(火) 04:29
最近「ComicGlassのソートがWindowsの並びと違う」とご意見を頂きました。
WindowsXP以降ではファイル名のソート順が数値などを解釈する自然な並びになるようになっています。
よって単純なコードの比較とは違う結果になります。
よく説明されるのは、
20string
2string
3string
というソート順だったのが
2string
3string
20string
という感じになることでしょうか。
WindowsであればStrCmpLogicalW()というAPIを呼び出すとこのソートが行われます。
Windows以外の環境でこのソートを再現する方法は、検索するといくつか提案されていますが、どうも結果が違います。
公式な仕様が見つかればいいんですが、どうも見つかりません。
そこで、それを再現すべくそもそもWindowsがどのようにソートしているか実験してみました。
(特に海外のサイトの情報はUnicode文字に関する配慮がほとんどされてない事が多いので)
結論から言うと、StrCmpLogicalWを再現するのって無理ゲーなんじゃないかと・・・。
いい方法あったら是非教えてください。
とりあえず、以下調査結果でございます。
・StrCmpLogicalWは記号、数値、その他文字の順で並ぶ
ASCIIやUnicodeでは「!」は「0~9」より前にあるが「;」などは数値より後です。
しかしながらWindowsでは「;」などの記号も数値より前に並びます。
(Unicodeコード順)
!test.txt
0test.txt
;test.txt
(Windows)
!test.txt
;test.txt
0test.txt
というわけで、その文字が「記号」であるか「数値」であるか「その他文字」であるかを区別しているようです。
そこで問題になるのが、何が記号で何が記号でないか。
(記号になる例)
!
;
®
µ
§
»
★
(記号にならない一例)
¼
À
うーん。規則性がわからん。
プログラムを組んでStrCmpLogicalWで実際に試してみたのですが、かなり後ろの方のコードまで記号扱いになるものとと文字扱いになるものが入り乱れています。
どうもUnicodeコンソーシアムが規定するUNICOD文字一覧表に記号かどうかの情報があるようです。
つまりUnicodeとして記号かどうかで判断されており、それは規則性などなく全一覧を知らないとどうにもならない。
まぁ、それだけなら全コードの情報を持っておけばいいのですが・・・
・・・しかし!
Windowsが知らない新しいUnicodeは正しく解釈していないような気がします・・・。
例えばU+2639(☹)、U+263A(☺)、U+263B(☻)は記号扱いですが、その他顔文字は記号になりません。
もしかしたらWindowsではサロゲートペアが正しく処理されていないだけかもしれません・・・。
これは心が折れますね・・・!
・StrCmpLogicalWは全角の記号も半角と記号と同じ扱いになる
同じ記号の場合は全角の方が後になる。
・・・ってことは正準等価性を使った正規化してるんでしょうかね?
その説を支持する結果として「»」が「§」よりも前に並ぶんですよね。コードは後ろなのに。
しかし後述しますが、数値で混在すると別々に評価されるっぽい。つまり単純に先に正規化しても駄目と・・・。
・StrCmpLogicalWは数字が並んでいる場合はまとめて1つの意味付けがされる
0001.txt
2.txt
003.txt
これはファイルに0から順に名前つけて桁が増えた時に変な順番になるのを防げます。
・StrCmpLogicalWは20桁以上の数値は数値として扱わない
符号なし64bitで表せる値の最大値は18,446,744,073,709,551,616で20桁なので、20桁目からオーバーフローします。
よって、Windowsも64bit演算で扱える最大値の19桁まで取り扱うようになっていると思われます。
これは実装が楽で助かります。
・StrCmpLogicalWは全角の0~9も数字として扱われる
・ただし半角全角混在の場合はそうならない。何故だ・・。
情報求む・・・・!
とりあえずASCIIコードにある記号だけ記号として解釈し、数値は19桁まで数値として認識する、という方法でだいたい近い結果にはなります。一致はしませんが。
Posted on 2014年10月24日(金) 01:00
タイトルのとおり。
どうも、Windows相手にNetBIOSの名前解決を試みると失敗することがあるようです。
ネットワーク・アナライザで見ると、リクエストは飛んでるのに応答が来ない。
発生条件は不明ですが、テストで変なリクエスト送ったあとに起きやすい気がするけど、何もしてなくても起きる時もある。
理由は不明ですが、一旦失敗するようになると、特定のIPアドレスから発信したものに対して応答が返らなくなる模様・・・。
全くそのままでIPアドレス変えると動くようになります。
このあたりエラーメッセージを簡略化してたので(接続できませんでしたくらいに)、次からメッセージ入れようと思います。
基本ここで失敗はしないと思っていたので。
どうもSMB/CIFS周りはロバスト性に欠けてやっかいですわー。
Windows同士でも調子わるいときありますもんね。
無駄にSMB/CIFS周り(のバッドノウハウ)に詳しくなっていく・・・。
Posted on 2014年9月19日(金) 16:24
とりあえず速報です。
iPhone6 Plus買いました。
でかいですね。
最初の設定時に気づいたのですが、「拡大モード」というのがあります。
シミュレータにはありませんでした。
その名のとおり、画面の要素全体が大きめになります。
アプリ側でそのような情報は来なかったと思うので、どのように対応しているかと思ったらピクセル数がiPhone6と同じになるようです。
さらにHOME画面も回転しなくなったりなど、まさにiPhone6のようになります。
よって、開発者はiPhone6PlusがあればiPhone6の解像度もテストできますよね。ステキ!
以下が検証結果です。
なんと!
iPhone6 Plusの拡大モードは、ピクセル数はiPhone6と同じですが、画面のscaleが@3x相当になってますね。
つまりまさかの新しいスクリーンサイズが今日新たに判明です! ステキ!
iPhone 6 シミュレータ
deviceName=x86_64
[UIScreen mainScreen].bounds=(375.000000,667.000000)
[UIScreen mainScreen].scale=2.000000
iPhone 6 Plusシミュレータ
deviceName=x86_64
[UIScreen mainScreen].bounds=(414.000000,736.000000)
[UIScreen mainScreen].scale=3.000000
iPhone 6 実機
deviceName=iPhone7,2
[UIScreen mainScreen].bounds=(375.000000,667.000000)
[UIScreen mainScreen].scale=2.000000
iPhone 6Plus 実機・標準モード
deviceName=iPhone7,1
[UIScreen mainScreen].bounds=(414.000000,736.000000)
[UIScreen mainScreen].scale=3.000000
iPhone 6Plus 実機・拡大モード
deviceName=iPhone7,1
[UIScreen mainScreen].bounds=(375.000000,667.000000)
[UIScreen mainScreen].scale=3.000000 ←New!
Posted on 2014年9月17日(水) 23:30
画面サイズの数字がよくわからなくなるので早見表作りました。
Model |
論理サイズ |
物理サイズ |
物理/論理比 |
widthxheight |
比率 |
近似 |
x1 |
x2 |
x3 |
Pixel |
4s以前 |
320×480 |
2:3 |
2:3 |
320×480 |
640×960 |
– |
640×960 |
1 |
5/5s |
320×568 |
40:71 |
9:16 |
– |
640×1136 |
– |
640×1136 |
1 |
6 |
375×667 |
375:667 |
9:16 |
– |
750×1334 |
– |
750×1334 |
1 |
6plus |
414×736 |
9:16 |
9:16 |
– |
– |
1242×2208 |
1080×1920 |
約0.870 |
iPad |
768×1024 |
3:4 |
3:4 |
768×1024 |
1536×2048 |
– |
1536×2048 |
1 |
物理サイズはRetinaモデルのものを書いてます。
iPhoneのx1サイズは3GSが最後ですので、新しいOSのみに対応するなら無いものと考えてOKです。
iPadはiPad mini(初代)がx1です。
iPhone6 Plusのみ論理座標と物理ピクセル数に差があります。
5sを基準として座標を変換するために倍率を計算しました。
(将来に備えてこの数字を直接使うのではなく、内部で計算したほうが幸せになれるとは思いますが)
Model |
横 |
縦 |
倍率 |
横 |
縦 |
だいたい |
5/5s |
320 |
568 |
1.0 |
1.0 |
1.0 |
6 |
375 |
667 |
1.171875 |
1.17429577465 |
1.173 |
6plus |
414 |
736 |
1.29375 |
1.29577464789 |
1.295 |
Posted on 2014年9月8日(月) 23:53
UTF-8って素晴らしいですよね。
unicodeは元々、世界中の全ての文字を16bitで表現するという英語圏以外の人からすればあまりに愚かな試みからスタートしましたが、もちろん破綻。
16bitを使いながら、サロゲートペアを使って16bit以上のコードも表現するようになりました。
結局サロゲートペアが必要な時点でUTF-16は欠点ばかりのものとなってしまいました。
最近よく使われるのはUTF-8です。ASCIIコードと互換性が保ちやすく大変便利ですね。
UTF-8は結局のところUTF-32をシリアライズしただけのものなんですが、へぇよく考えてるなぁという感じなのです。
任意のバイトストリームから混同せずに文字列を取り出せます。
ただ欠点もあって、色々処理をするときに文字ごとに長さが違うので厄介です。
そこでUTF-8から32bit表現(UTF-32)に変換するコードを書いてみました。
プログラムの内部的にはUTF-32を使うと捗りますので。
このようなUTF-8の欠点はサロゲートペアがある時点でUTF-16でも同様なんですが、サロゲートペアはあまり出てこないので無視してしまってる駄目な処理系もあるような気がします。
なお、セキュリティの観点からはUTF-8の冗長表現が問題になることもあります。
本来ASCIIコードである文字でももっと大きなバイス数で表現することが出来てしまうので不正文字チェックを抜けてしまうということです。
例えば0xC0 0xAF(11000000 10101111)は、2バイトのUTF-8として扱うと[101111]の部分がデータになりますので、0x2F(バックスラッシュ)と等価になります。
つまりより小さいバイトのコードは大きなバイト数でも表現出来てしまうため、単純チェックだとすり抜けるということです。
仕様ではこのようなバイト列は不正なUTF-8バイト列としなければなりません。
また5バイトと6バイトのUTF-8も現在ではUnicodeとしては不正とされていますがISO/IEC 10646では存在しています。
以下のコードではそのような不正なUTF-8もそのまま変換します。
unsigned long utf8ToUtf32(unsigned char *input, int *bytesInSequence)
{
unsigned char c1, c2, c3, c4, c5, c6;
*bytesInSequence = 1;
if (!input)
{
return 0;
}
//0xxxxxxx (ASCII) 7bit
c1 = input[0];
if ((c1 & 0x80) == 0x00)
{
return c1;
}
//10xxxxxx high-order byte
if ((c1 & 0xc0) == 0x80)
{
return 0;
}
//0xFE or 0xFF BOM (not utf-8)
if (c1 == 0xfe || c1 == 0xFF )
{
return 0;
}
//110AAAAA 10BBBBBB 5+6bit=11bit
c2 = input[1];
if (((c1 & 0xe0) == 0xc0) &&
((c2 & 0xc0) == 0x80))
{
*bytesInSequence = 2;
return ((c1 & 0x1f) << 6) | (c2 & 0x3f);
}
//1110AAAA 10BBBBBB 10CCCCCC 4+6*2bit=16bit
c3 = input[2];
if (((c1 & 0xf0) == 0xe0) &&
((c2 & 0xc0) == 0x80) &&
((c3 & 0xc0) == 0x80))
{
*bytesInSequence = 3;
return ((c1 & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f);
}
//1111 0AAA 10BBBBBB 10CCCCCC 10DDDDDD 3+6*3bit=21bit
c4 = input[3];
if (((c1 & 0xf8) == 0xf0) &&
((c2 & 0xc0) == 0x80) &&
((c3 & 0xc0) == 0x80) &&
((c4 & 0xc0) == 0x80))
{
*bytesInSequence = 4;
return ((c1 & 0x07) << 18) | ((c2 & 0x3f) << 12) | ((c3 & 0x3f) << 6) | (c4 & 0x3f);
}
//1111 00AA 10BBBBBB 10CCCCCC 10DDDDDD 10EEEEEE 2+6*4bit=26bit
c5 = input[4];
if (((c1 & 0xfc) == 0xf0) &&
((c2 & 0xc0) == 0x80) &&
((c3 & 0xc0) == 0x80) &&
((c4 & 0xc0) == 0x80) &&
((c5 & 0xc0) == 0x80))
{
*bytesInSequence = 4;
return ((c1 & 0x03) << 24) | ((c2 & 0x3f) << 18) | ((c3 & 0x3f) << 12) | ((c4 & 0x3f) << 6) | (c5 & 0x3f);
}
//1111 000A 10BBBBBB 10CCCCCC 10DDDDDD 10EEEEEE 10FFFFFF 1+6*5bit=31bit
c6 = input[5];
if (((c1 & 0xfe) == 0xf0) &&
((c2 & 0xc0) == 0x80) &&
((c3 & 0xc0) == 0x80) &&
((c4 & 0xc0) == 0x80) &&
((c5 & 0xc0) == 0x80) &&
((c6 & 0xc0) == 0x80))
{
*bytesInSequence = 4;
return ((c1 & 0x01) << 30) | ((c2 & 0x3f) << 24) | ((c3 & 0x3f) << 18) | ((c4 & 0x3f) << 12) | ((c5 & 0x3f) << 6) | (c6 & 0x3f);
}
return 0;
}
Posted on 2014年9月2日(火) 23:07
昨日、ComicGlassのアップデートのためiTunes connectにアプリを送信したところ、CFBundleShortVersionStringが無いということで警告が出ました。(警告は出ましたがアップロード自体は受理されてステータスはWaiting For Reviewに)
ComicGlassを最初に作ったのはiOS3の頃です。その時にはCFBundleShortVersionStringが無かったようでxcodeで自動的に作られなかったので今までずっと無いままでした。CFBundleShortVersionString無いなーとは思ってたんですが、バージョンアップのとき変更するの一つでいいし楽だからいいやと放置してました。
前回までは警告出なかったので最近警告が出るようになったんだと思います。
このCFBundleShortVersionStringですが、本来はピリオドで区切った3つの数字を用いるのが正しい表記のようです。10.0.1みたいな感じですね。現在ComicGlassのバージョン表記は7.50といった2数字表記になっているのでいつか改めないといけないかもしれません。警告が出るようになったということは今後厳格にチェックが入る前触れかもしれないので・・・。
なお、現在のところCFBundleShortVersionStringが3数字になっていなくても(7.50.1ではなく7.50とかでも)警告なく通るようです。
新たにアプリ作るときは正しいフォーマットでバージョン番号を付けたほうが良いと思われます。
お気をつけください。