プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

MacアドレスからVenderを調べるには二分探索…とも言い切れない

MacアドレスはCPUに対して一意(まれに適当で重複があったりすることがあります)に付与されていてその上位6桁(24bit)がVenderコードになっています。
MacアドレスとVenderの対応はIEEEにリスト化されているので、これを用いてMacアドレスからVenderを探すことが出来ます。
http://standards-oui.ieee.org/oui/oui.txt

ここで、Macアドレス/Venderのリストは約2万9000件あります。

一方、Macアドレス/Venderリストは16進数であらわされた数字であり、順番に並んでいます。

とすると、キー(Macアドレス)と値(Vender Name)の組み合わせで、キーに対して単調増加のListとなります。
単調増加のキーに対して値を探すのは二分探索O(logn)をするのがセオリーで、どこかでそのようなコードを見た気がしますが、辞書にするとO(1)なのでこの方針で実装します。

まずはoui.txtからMacアドレスとVenderの辞書を作成します。
f:id:s51517765:20210109130200p:plain
このtextファイルの中から、Macアドレスが6桁であらわされた行を正規表現で取得すると、先頭6文字がMacアドレスでタブ以降がVender Nameとなります。

using System.Collections.Generic;

private void Read_MacAddressList()
{
    string fileName;
    fileName = "oui.txt";
    if (!File.Exists(fileName))
    {
        MessageBox.Show(fileName + " not find!");
        return;
    }
    try
    {
        StreamReader sr = new StreamReader(fileName, Encoding.GetEncoding("UTF-8"));
        string tmp;
        string mac;
        string vender;

        while (true)
        {
            if (sr.EndOfStream) return;
            tmp = sr.ReadLine();
            if (tmp.Length < 8) continue;

            mac = tmp.Substring(0, 6);
            if (!System.Text.RegularExpressions.Regex.IsMatch(mac, @"[\dabcdefABCDEF]{6}")) continue;
            vender = tmp.Substring(7).Trim();
            vender = vender.Substring(vender.IndexOf("\t"));
            vender = vender.Replace("\t", "");
            if (MacAddressList.ContainsKey(mac)) continue;
            MacAddressList.Add(mac, vender);
        }
    }
    catch
    {
        MessageBox.Show("File Open Error!");
    }
}

Macアドレス:区切りで与えられる文字列とすると、その先頭6桁を正規表現で取得して辞書を検索します。

private string Get_VenderName(string macAdrres)
{
    try
    {
        System.Text.RegularExpressions.MatchCollection Regs = System.Text.RegularExpressions.Regex.Matches(macAdrres, @"[\dabcdefABCDEF]{2}");
        string mac = "";
        mac = Regs[0].Value + Regs[1].Value + Regs[2].Value;
        return MacAddressList[mac];
    }
    catch
    {
        return "";
    }
}

まとめ

MacアドレスからVenderを探すコードを実装しました。
二分探索O(logn)をするのがセオリーで、どこかでそのようなコードを見た気がしますが一度辞書を作ってしまえばそのほうが速くなりそうです。
また、2万9000件であってもこれ自体は必ずしも二分探索を必要とする量ではないと思います。
AtCorderでタイムアウトするのがだいたい200万件以上(言語によっても実装によっても異なるが)なので実は十分短時間で処理できます。
ただし、ファイルから読み込む速度はもっと遅かったり、リストの内容が毎回変わったり、1秒間に数百回探索したり、CPUが非力だったりなどの条件によっては異なるかもしれません。