やらかしコーディング集（フィクション）を作ろうと思ったがまだ２個しかできていない件

こんなコード書いたら不具合になるよなーと思ったコードを羅列してみようと思ったが、
あまりネタがなかった＆不具合再現させようとしたら案外面倒だったので記事作成が進まない。。。
が、せっかく書いたので投稿してみる。

何がダメか考えてから解説に行ってみましょう。
C#で書いていますが、大体の言語で当てはまるかと。

# Q-1. NGコード

現在時刻を取得して文字列にしようとしている。

```csharp

static string GetNow()
{
    int s = DateTime.Now.Second;
    int ms = DateTime.Now.Millisecond;
    return string.Format("{0:00}.{1:000}", s, ms);
}
```

[解説へ](https://qiita.com/kob58im/items/3a138103682a1a84c904#a-1-%E5%A4%89%E5%8C%96%E3%81%99%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%89%B1%E3%81%86%E3%81%A8%E3%81%8D%E3%81%AF%E6%B0%97%E3%82%92%E4%BB%98%E3%81%91%E3%82%88%E3%81%86)

# Q-2. NGコード

ユーザーからの文字列入力に対して、文字数制限を設けている。

```csharp

static readonly int MaxLen = 10;
void AlertLength()
{
    string s1 = TextBox1.Text;
    if ( s1.Length > MaxLen ) {
        MessageBox.Show("文字数は"+MaxLen.ToString()+"以下にしてください。");
    }
}
```

以下のシーンを想定してください。
「ユーザが`今日パパと𩸽を食べた`という文字列を入力したとき。」

<b>※上記`文字列`は意図的に危ない文字データを含んでいるので取り扱い注意。</b>
（考慮がされていないソフトやウェブサイト、会社のシステムなどに入力すると危険な動作をする恐れがあります。メモリ破壊して想定できない動作をする可能性もあります。）

[解説へ](https://qiita.com/kob58im/items/3a138103682a1a84c904#a-2-%E7%9F%A5%E3%82%89%E3%81%AA%E3%81%84%E3%81%A8%E6%80%96%E3%81%84%E3%82%B5%E3%83%AD%E3%82%B2%E3%83%BC%E3%83%88%E3%83%9A%E3%82%A2%E3%81%A8%E7%B5%90%E5%90%88%E6%96%87%E5%AD%97%E3%81%AE%E8%A9%B1)

# Q-3. 予定地

# Q-4. 予定地

# Q-5. 予定地


# A-1. 変化するデータを扱うときは気を付けよう

ダメな点：`DateTime.Now`が2回呼ばれる間に、`DateTime.Now`の値が変化してしまう。
何が起きるか：例えば 12.999～13.000秒の間でこのメソッドを実行したとき、`"12.000"` を出力してしまう可能性がある。

<details><summary>修正前コードの不具合再現用コード</summary><div>

```csharp:不具合発生再現用コード

using System;
using System.Diagnostics;
using System.Threading;

class DateTimeTest
{
    static string GetNow(out int os, out int oms)
    {
        int s = DateTime.Now.Second;
        int ms = DateTime.Now.Millisecond;
        os = s;
        oms = ms;
        return string.Format("{0:00}.{1:000}", s, ms);
    }

    [STAThread]
    static void Main(string[] args)
    {
        string prevStr = "no data";
        int prevTs;
        int prevTms;
        int ts;
        int tms;

        Console.WriteLine("started.");
        for (int i=0;i<1000;i++) {
            string nowStr = GetNow(out ts, out tms);
            prevStr = nowStr;
            while ( true ) {
                prevTs = ts;
                prevTms = tms;
                nowStr = GetNow(out ts, out tms);
                Console.Write(tms);
                Console.Write("\r");
                if ( (ts!=prevTs) && tms == 0 ) { // xx.xxx から yy.000 になったときに表示
                    Console.Write("i:");
                    Console.WriteLine(i);

                    Console.WriteLine(prevStr);
                    Console.WriteLine(nowStr);
                    break;
                }
                prevStr = nowStr;
            }
        }
    }
}
```
</div></details>
※色々コードが付加されているのはご容赦ください。

```text:出力結果抜粋

i:20
05.999
06.000
i:21
06.000   <-- ここで6.999と出力すべきところが、millisecのほうが進みすぎて6.000となっている
07.000
i:22
07.999
08.000
```

## A-1.1 - 対応例
変化するデータを使うときは、一時変数なりに一度代入しよう。
※マルチスレッドや割り込みなどで値が変化するデータを扱う場合は、これだけでは不十分。

```csharp:修正版コード

static string GetNow()
{
    var t = DateTime.Now;
    return string.Format("{0:00}.{1:000}", t.Second, t.Millisecond);
}
```

# A-2. 知らないと怖いサロゲートペアと結合文字の話

例で挙げた文字列は、サロゲートペアと、結合文字を含んでおり、
`"今日パパと𩸽を食べた".Length`は見かけは10文字だが、12を返す。

## A-2-1. サロゲートペア
C#(.NET)を含め、Windowsの文字列の内部表現は<b>UTF-16</b>となっている(Unicodeの符号化方式のうちの1つ)。
UTF-16では、通常2byteで1文字を表現する[^1]が、2byte(0～65535)では世の中の文字を表現しきれず、<b>2文字分[^2](4byte)を使用する文字がある。(サロゲートペア)</b>
今回の例の`𩸽`(ほっけ)は、`"\uD867\uDE3D"`。
絵文字もサロゲートペアだったりする。

## A-2-2. 結合文字
Unicodeでは、見かけ上は１文字（例：「パ」）であっても、2つの文字「ハ」＋「゜」[^3]を表示上1文字に見えるように扱っている場合がある。
UTF-16では：
`パ`：`"\u30D1"`
`パ`(ハ＋゜)：`"\u30CF\u309A"` (`"\u30CF"`は`ハ`)

## A-2-2-1. 参考
+ [結合文字 - Wikipedia](https://ja.wikipedia.org/wiki/結合文字)
+ [.NETでのUnicode合成文字の処理について調べた - 日経XTECH](https://tech.nikkeibp.co.jp/it/article/COLUMN/20070115/258726/)

## A-2-3. どうすればよいか

ユーザが上記のようなデータを入力し得る場合、下記のような点に注意が必要になると思います。
+ バッファサイズ(`StringBuilder`や保存先のレコード長)は、実際の内部表現に必要なサイズを確保する必要がある。（当然ではあるが。）
+ `String`クラスのメソッド(`Length`,`Substring`,`IndexOf`など)は、内部の文字配列に対する操作であり、人が認識する「文字」単位ではない。（`Regex`クラスも同様のはず。）

サロゲートペアや結合文字を含む処理のしかた：
https://dobon.net/vb/dotnet/string/substring.html#substringbytextelements

文字数制限については、ユーザー側に特殊な文字は使わないように喚起するなどもありかもしれないが（システムや想定ユーザーによるが。。）、入力できてしまう以上は、考慮した設計・実装が必要である。

# ねたメモ

・浮動小数点数
・メモリ関係
・マルチスレッド(書けない気がする)

[^1]: 半角・全角問わず。

[^2]: 「2文字」という表現は適切ではないかも。（サロゲートペアの上下各2byteは「文字」ではないので。）

[^3]: 独立した半濁点「゜」と、結合文字に使われる半濁点は区別されます。便宜上、説明文では独立した半濁点「゜」を使っています。