すべてのプロダクト
Search
ドキュメントセンター

MaxCompute:MaxCompute UDF による IPv4 または IPv6 アドレスの所在地変換

最終更新日:Jun 22, 2026

ビッグデータプラットフォームの発展に伴い、非構造化データや半構造化データなど、さまざまな種類のデータを処理できるようになりました。中でも、IP アドレスを所在地に変換することは、よくあるシナリオです。本稿では、MaxCompute のユーザー定義関数 (UDF) を使用して IPv4 または IPv6 アドレスを所在地に変換する方法について説明します。

背景情報

IPv4 または IPv6 アドレスを所在地に変換するには、IP アドレスライブラリが必要です。IP アドレスライブラリファイルをダウンロードし、リソースとして MaxCompute プロジェクトにアップロードする必要があります。MaxCompute UDF を開発し、IP アドレスライブラリファイルに基づいて関数を登録することで、SQL ステートメントで関数を呼び出して IP アドレスを所在地に変換できます。

重要

この記事で提供する IP アドレスライブラリファイルは、このベストプラクティスを検証するためにのみ使用されます。実際のビジネス状況に合わせて、IP アドレスライブラリファイルをご自身で管理してください。

前提条件

操作手順

MaxCompute UDF を使用して IPv4 または IPv6 アドレスを所在地に変換する手順は、次のとおりです。

  1. ステップ 1:IP アドレスライブラリファイルのアップロード

    IP アドレスライブラリファイルをリソースとして MaxCompute プロジェクトにアップロードします。後ほど作成する MaxCompute UDF は、このリソースに依存します。

  2. ステップ 2:プロジェクトへの接続

    MaxCompute プロジェクトに接続し、MaxCompute Java モジュールを作成します。

  3. ステップ 3:MaxCompute UDF の作成

    IntelliJ IDEA で MaxCompute UDF コードを作成します。

  4. ステップ 4:MaxCompute UDF の登録

    MaxCompute UDF を関数として登録します。

  5. ステップ 5:MaxCompute UDF の呼び出しによる IP アドレスの所在地変換

    SQL ステートメントで登録した関数を呼び出して、IP アドレスを所在地に変換します。

ステップ 1:IP アドレスライブラリファイルのアップロード

  1. IP アドレスライブラリファイルをローカルにダウンロードして解凍し、ipv4.txt と ipv6.txt を取得して、MaxCompute クライアントのインストールディレクトリ ...\odpscmd_public\bin に配置します。

  2. MaxCompute クライアントをインストールしてログオンし、目的の MaxCompute プロジェクトに移動します。

  3. add file コマンドを実行して、ipv4.txt と ipv6.txt を File タイプのリソースとして MaxCompute プロジェクトにアップロードします。

    ADD file ipv4.txt -f;
    ADD file ipv6.txt -f;

    リソースの追加に関する詳細については、「リソースの管理」をご参照ください。

  4. (ローカルデバッグ用) ipv4.txt と ipv6.txt をローカルプロジェクトの warehouse/example_project/_resources_ ディレクトリにコピーします。

ステップ 2:プロジェクトへの接続

  1. MaxCompute プロジェクトに接続します。詳細な手順については、「プロジェクト接続の管理」をご参照ください。

  2. MaxCompute Java モジュールを作成します。詳細な手順については、「MaxCompute Java モジュールの作成」をご参照ください。

ステップ 3:MaxCompute UDF の作成

  1. Java Class オブジェクトを作成します。

    後続のステップで記述する MaxCompute UDF コードでは、ここで作成した Java Class を使用します。

    1. IntelliJ IDEA の画面で、[Project] 領域のモジュールのソースディレクトリ (つまり src > main > java) を右クリックし、[New] > [Java Class] を選択します。

    2. [New Java Class] ダイアログボックスでクラス名を入力し、Enter キーを押してコード編集領域にコードを入力します。

      3 つの Java Class オブジェクトを順番に作成する必要があります。クラス名と対応するコードは次のとおりです。コードは変更せずに、そのままコピーして使用できます。

      • IpUtils

        package com.aliyun.odps.udf.utils;
        import java.math.BigInteger;
        import java.net.Inet4Address;
        import java.net.Inet6Address;
        import java.net.InetAddress;
        import java.net.UnknownHostException;
        import java.util.Arrays;
        public class IpUtils {
            /**
             * 文字列形式の IP アドレスを long に変換します
             *
             * @param ipInString
             * 文字列形式の IP アドレス
             * @return long 形式の IP アドレスを返します
             */
            public static long StringToLong(String ipInString) {
                ipInString = ipInString.replace(" ", "");
                byte[] bytes;
                if (ipInString.contains(":"))
                    bytes = ipv6ToBytes(ipInString);
                else
                    bytes = ipv4ToBytes(ipInString);
                BigInteger bigInt = new BigInteger(bytes);
        //        System.out.println(bigInt.toString());
                return bigInt.longValue();
            }
            /**
             * 文字列形式の IP アドレスを long に変換します
             *
             * @param ipInString
             * 文字列形式の IP アドレス
             * @return BigInt の文字列形式の IP アドレス
             */
            public static String StringToBigIntString(String ipInString) {
                ipInString = ipInString.replace(" ", "");
                byte[] bytes;
                if (ipInString.contains(":"))
                    bytes = ipv6ToBytes(ipInString);
                else
                    bytes = ipv4ToBytes(ipInString);
                BigInteger bigInt = new BigInteger(bytes);
                return bigInt.toString();
            }
            /**
             * 整数形式の IP アドレスを文字列形式に変換します
             *
             * @param ipInBigInt
             * 整数形式の IP アドレス
             * @return 文字列形式の IP アドレス
             */
            public static String BigIntToString(BigInteger ipInBigInt) {
                byte[] bytes = ipInBigInt.toByteArray();
                byte[] unsignedBytes = Arrays.copyOfRange(bytes, 1, bytes.length);
                // 符号ビットを削除
                try {
                    String ip = InetAddress.getByAddress(unsignedBytes).toString();
                    return ip.substring(ip.indexOf('/') + 1).trim();
                } catch (UnknownHostException e) {
                    throw new RuntimeException(e);
                }
            }
            /**
             * IPv6 アドレスを符号付き byte[17] に変換
             */
            private static byte[] ipv6ToBytes(String ipv6) {
                byte[] ret = new byte[17];
                ret[0] = 0;
                int ib = 16;
                boolean comFlag = false;// IPv4 混合モードのフラグ
                if (ipv6.startsWith(":"))// 先頭のコロンを削除
                    ipv6 = ipv6.substring(1);
                String groups[] = ipv6.split(":");
                for (int ig = groups.length - 1; ig > -1; ig--) {// 逆方向にスキャン
                    if (groups[ig].contains(".")) {
                        // IPv4 混合モードが出現
                        byte[] temp = ipv4ToBytes(groups[ig]);
                        ret[ib--] = temp[4];
                        ret[ib--] = temp[3];
                        ret[ib--] = temp[2];
                        ret[ib--] = temp[1];
                        comFlag = true;
                    } else if ("".equals(groups[ig])) {
                        // ゼロ圧縮が出現、欠落しているグループ数を計算
                        int zlg = 9 - (groups.length + (comFlag ? 1 : 0));
                        while (zlg-- > 0) {// これらのグループを 0 に設定
                            ret[ib--] = 0;
                            ret[ib--] = 0;
                        }
                    } else {
                        int temp = Integer.parseInt(groups[ig], 16);
                        ret[ib--] = (byte) temp;
                        ret[ib--] = (byte) (temp >> 8);
                    }
                }
                return ret;
            }
            /**
             * IPv4 アドレスを符号付き byte[5] に変換
             */
            private static byte[] ipv4ToBytes(String ipv4) {
                byte[] ret = new byte[5];
                ret[0] = 0;
                // まず IP アドレス文字列内の「.」の位置を検索
                int position1 = ipv4.indexOf(".");
                int position2 = ipv4.indexOf(".", position1 + 1);
                int position3 = ipv4.indexOf(".", position2 + 1);
                // 各ドット間の文字列を整数に変換
                ret[1] = (byte) Integer.parseInt(ipv4.substring(0, position1));
                ret[2] = (byte) Integer.parseInt(ipv4.substring(position1 + 1,
                        position2));
                ret[3] = (byte) Integer.parseInt(ipv4.substring(position2 + 1,
                        position3));
                ret[4] = (byte) Integer.parseInt(ipv4.substring(position3 + 1));
                return ret;
            }
            /**
             * @param ipAdress IPv4 または IPv6 文字列
             * @return 4:IPv4、6:IPv6、0:不正なアドレス
             * @throws Exception
             */
            public static int isIpV4OrV6(String ipAdress) throws Exception {
                InetAddress address = InetAddress.getByName(ipAdress);
                if (address instanceof Inet4Address)
                    return 4;
                else if (address instanceof Inet6Address)
                    return 6;
                return 0;
            }
            /*
             * IP が特定の IP 範囲に属するかどうかを検証する
             *
             * ipSection: IP 範囲 ('-' で区切られた)
             *
             * ip: 検証対象の IP アドレス
             */
            public static boolean ipExistsInRange(String ip, String ipSection) {
                ipSection = ipSection.trim();
                ip = ip.trim();
                int idx = ipSection.indexOf('-');
                String beginIP = ipSection.substring(0, idx);
                String endIP = ipSection.substring(idx + 1);
                return getIp2long(beginIP) <= getIp2long(ip)
                        && getIp2long(ip) <= getIp2long(endIP);
            }
            public static long getIp2long(String ip) {
                ip = ip.trim();
                String[] ips = ip.split("\\.");
                long ip2long = 0L;
                for (int i = 0; i < 4; ++i) {
                    ip2long = ip2long << 8 | Integer.parseInt(ips[i]);
                }
                return ip2long;
            }
            public static long getIp2long2(String ip) {
                ip = ip.trim();
                String[] ips = ip.split("\\.");
                long ip1 = Integer.parseInt(ips[0]);
                long ip2 = Integer.parseInt(ips[1]);
                long ip3 = Integer.parseInt(ips[2]);
                long ip4 = Integer.parseInt(ips[3]);
                long ip2long = 1L * ip1 * 256 * 256 * 256 + ip2 * 256 * 256 + ip3 * 256
                        + ip4;
                return ip2long;
            }
            public static void main(String[] args) {
                System.out.println(StringToLong("2002:7af3:f3be:ffff:ffff:ffff:ffff:ffff"));
                System.out.println(StringToLong("54.38.XX.XX"));
            }
            private class Invalid{
                private Invalid()
                {
                }
            }
        }
                                                
      • IpV4Obj

        package com.aliyun.odps.udf.objects;
        public class IpV4Obj {
            public long startIp ;
            public long endIp ;
            public String city;
            public String province;
            public IpV4Obj(long startIp, long endIp, String city, String province) {
                this.startIp = startIp;
                this.endIp = endIp;
                this.city = city;
                this.province = province;
            }
            @Override
            public String toString() {
                return "IpV4Obj{" +
                        "startIp=" + startIp +
                        ", endIp=" + endIp +
                        ", city='" + city + '\'' +
                        ", province='" + province + '\'' +
                        '}';
            }
            public void setStartIp(long startIp) {
                this.startIp = startIp;
            }
            public void setEndIp(long endIp) {
                this.endIp = endIp;
            }
            public void setCity(String city) {
                this.city = city;
            }
            public void setProvince(String province) {
                this.province = province;
            }
            public long getStartIp() {
                return startIp;
            }
            public long getEndIp() {
                return endIp;
            }
            public String getCity() {
                return city;
            }
            public String getProvince() {
                return province;
            }
        }
                                                
      • IpV6Obj

        package com.aliyun.odps.udf.objects;
        public class IpV6Obj {
            public String startIp ;
            public String endIp ;
            public String city;
            public String province;
            public String getStartIp() {
                return startIp;
            }
            @Override
            public String toString() {
                return "IpV6Obj{" +
                        "startIp='" + startIp + '\'' +
                        ", endIp='" + endIp + '\'' +
                        ", city='" + city + '\'' +
                        ", province='" + province + '\'' +
                        '}';
            }
            public IpV6Obj(String startIp, String endIp, String city, String province) {
                this.startIp = startIp;
                this.endIp = endIp;
                this.city = city;
                this.province = province;
            }
            public void setStartIp(String startIp) {
                this.startIp = startIp;
            }
            public String getEndIp() {
                return endIp;
            }
            public void setEndIp(String endIp) {
                this.endIp = endIp;
            }
            public String getCity() {
                return city;
            }
            public void setCity(String city) {
                this.city = city;
            }
            public String getProvince() {
                return province;
            }
            public void setProvince(String province) {
                this.province = province;
            }
        }
                                                
  2. MaxCompute UDF コードを作成します。

    1. [Project] 領域で、モジュールのソースディレクトリ (つまり src > main > java) を右クリックし、[New] > [MaxCompute Java] を選択します。

    2. [Create new MaxCompute java class] ダイアログボックスで、[UDF] をクリックし、[Name] を入力してから Enter キーを押し、コード編集領域にコードを入力します。

      たとえば、Java Class 名は IpLocation です。コードの内容は次のとおりです。コードは変更せずに、そのままコピーして使用できます。

      package com.aliyun.odps.udf.udfFunction;
      import com.aliyun.odps.udf.ExecutionContext;
      import com.aliyun.odps.udf.UDF;
      import com.aliyun.odps.udf.UDFException;
      import com.aliyun.odps.udf.utils.IpUtils;
      import com.aliyun.odps.udf.objects.IpV4Obj;
      import com.aliyun.odps.udf.objects.IpV6Obj;
      import java.io.*;
      import java.util.ArrayList;
      import java.util.Comparator;
      import java.util.List;
      import java.util.stream.Collectors;
      public class IpLocation extends UDF {
          public static IpV4Obj[] ipV4ObjsArray;
          public static IpV6Obj[] ipV6ObjsArray;
          public IpLocation() {
              super();
          }
          @Override
          public void setup(ExecutionContext ctx) throws UDFException, IOException {
              //IPV4
              if(ipV4ObjsArray==null)
              {
                  BufferedInputStream bufferedInputStream = ctx.readResourceFileAsStream("ipv4.txt");
                  BufferedReader br = new BufferedReader(new InputStreamReader(bufferedInputStream));
                  ArrayList<IpV4Obj> ipV4ObjArrayList=new ArrayList<>();
                  String line = null;
                  while ((line = br.readLine()) != null) {
                      String[] f = line.split("\\|", -1);
                      if(f.length>=5)
                      {
                          long startIp = IpUtils.StringToLong(f[0]);
                          long endIp = IpUtils.StringToLong(f[1]);
                          String city=f[3];
                          String province=f[4];
                          IpV4Obj ipV4Obj = new IpV4Obj(startIp, endIp, city, province);
                          ipV4ObjArrayList.add(ipV4Obj);
                      }
                  }
                  br.close();
                  List<IpV4Obj> collect = ipV4ObjArrayList.stream().sorted(Comparator.comparing(IpV4Obj::getStartIp)).collect(Collectors.toList());
                  ArrayList<IpV4Obj> basicIpV4DataList=(ArrayList)collect;
                  IpV4Obj[] ipV4Objs = new IpV4Obj[basicIpV4DataList.size()];
                  ipV4ObjsArray = basicIpV4DataList.toArray(ipV4Objs);
              }
              //IPV6
              if(ipV6ObjsArray==null)
              {
                  BufferedInputStream bufferedInputStream = ctx.readResourceFileAsStream("ipv6.txt");
                  BufferedReader br = new BufferedReader(new InputStreamReader(bufferedInputStream));
                  ArrayList<IpV6Obj> ipV6ObjArrayList=new ArrayList<>();
                  String line = null;
                  while ((line = br.readLine()) != null) {
                      String[] f = line.split("\\|", -1);
                      if(f.length>=5)
                      {
                          String startIp = IpUtils.StringToBigIntString(f[0]);
                          String endIp = IpUtils.StringToBigIntString(f[1]);
                          String city=f[3];
                          String province=f[4];
                          IpV6Obj ipV6Obj = new IpV6Obj(startIp, endIp, city, province);
                          ipV6ObjArrayList.add(ipV6Obj);
                      }
                  }
                  br.close();
                  List<IpV6Obj> collect = ipV6ObjArrayList.stream().sorted(Comparator.comparing(IpV6Obj::getStartIp)).collect(Collectors.toList());
                  ArrayList<IpV6Obj> basicIpV6DataList=(ArrayList)collect;
                  IpV6Obj[] ipV6Objs = new IpV6Obj[basicIpV6DataList.size()];
                  ipV6ObjsArray = basicIpV6DataList.toArray(ipV6Objs);
              }
          }
          public String evaluate(String ip){
              if(ip==null||ip.trim().isEmpty()||!(ip.contains(".")||ip.contains(":")))
              {
                  return null;
              }
              int ipV4OrV6=0;
              try {
                  ipV4OrV6= IpUtils.isIpV4OrV6(ip);
              } catch (Exception e) {
                  return null;
              }
              //IPv4 の場合
              if(ipV4OrV6==4)
              {
                  int i = binarySearch(ipV4ObjsArray, IpUtils.StringToLong(ip));
                  if(i>=0)
                  {
                      IpV4Obj ipV4Obj = ipV4ObjsArray[i];
                      return ipV4Obj.city+","+ipV4Obj.province;
                  }else{
                      return null;
                  }
              }else if(ipV4OrV6==6)//IPv6 の場合
              {
                  int i = binarySearchIPV6(ipV6ObjsArray, IpUtils.StringToBigIntString(ip));
                  if(i>=0)
                  {
                      IpV6Obj ipV6Obj = ipV6ObjsArray[i];
                      return ipV6Obj.city+","+ipV6Obj.province;
                  }else{
                      return null;
                  }
              }else{//IPv4 または IPv6 形式でない場合
                  return null;
              }
          }
          @Override
          public void close() throws UDFException, IOException {
              super.close();
          }
          private static int binarySearch(IpV4Obj[] array,long ip){
              int low=0;
              int hight=array.length-1;
              while (low<=hight)
              {
                  int middle=(low+hight)/2;
                  if((ip>=array[middle].startIp)&&(ip<=array[middle].endIp))
                  {
                      return middle;
                  }
                  if (ip < array[middle].startIp)
                      hight = middle - 1;
                  else {
                      low = middle + 1;
                  }
              }
              return -1;
          }
          private static int binarySearchIPV6(IpV6Obj[] array,String ip){
              int low=0;
              int hight=array.length-1;
              while (low<=hight)
              {
                  int middle=(low+hight)/2;
                  if((ip.compareTo(array[middle].startIp)>=0)&&(ip.compareTo(array[middle].endIp)<=0))
                  {
                      return middle;
                  }
                  if (ip.compareTo(array[middle].startIp) < 0)
                      hight = middle - 1;
                  else {
                      low = middle + 1;
                  }
              }
              return -1;
          }
          private class Invalid{
              private Invalid()
              {
              }
          }
      }
                                      
  3. ローカルデバッグ用のデータを準備します。

    1. ローカルプロジェクトの warehouse/example_project/__tables__/wc_in2/p1=2/p2=1/ ディレクトリで、[data] ファイルを開きます。

    2. [data] ファイルの最後の列のデータを ipv4.txt 内の IP アドレスに変更して (ipv4.txt から任意の 3 つの IP アドレスを選択して入力できます)、保存します。

  4. MaxCompute UDF をデバッグして、コードが正常に実行できることを確認します。

    デバッグ操作の詳細については、「ローカル実行による UDF のデバッグ」をご参照ください。

    1. 作成した MaxCompute UDF スクリプトを右クリックし、[Run] を選択します。

    2. [Run/Debug Configurations] ダイアログボックスで、実行パラメーターを設定して、[OK] をクリックします。

      実行パラメーターの設定例:[Name]IpLocation に、[Main class]com.aliyun.odps.udf.udfFunction.IpLocation に設定します。MaxCompute 固有のフィールド:[*MaxCompute project]localexample_project を選択し、[*MaxCompute table]wc_in2 を入力し、[*Table partition]p1=2,p2=1 (形式: p1=1,p2=2) を入力し、[*Table columns]colc (形式: c1,c2) を入力します。[Download Record limit]100 に、[Data Column Separator] をカンマに設定します。設定が完了したら、[OK] をクリックします。エラーが返されなければ、コードの実行は成功です。後続のステップに進むことができます。エラーが発生した場合は、IntelliJ IDEA のエラーメッセージに従って対処してください。

      説明

      実行パラメーターは、図のデータを参照して入力できます。

ステップ 4:MaxCompute UDF の登録

  1. コンパイルに成功した MaxCompute UDF スクリプトを右クリックして、[Deploy to server…] を選択します。

  2. [Package a jar, submit resource and register function] ダイアログボックスで、パラメーター情報を設定します。

    パラメーターの詳細については、「パッケージ化、アップロード、登録」をご参照ください。[MaxCompute project] で対象のプロジェクト (例:doc_test_dev) を選択し、[Resource file] でパッケージ化された JAR ファイルを選択します。[Resource name] は自動的に udf-1.0-SNAPSHOT.jar と入力されます。[Extra resources] で必要な追加リソースファイル (例:ipv4.txtipv6.txt) を選択し、[Main class] に UDF メインクラスの完全修飾名 (例:com.aliyun.odps.udf.udfFunction.IpLocation) を入力し、[Function name] に関数名 (例:ipv4_ipv6_aton) を入力します。[Force update if already exists] にチェックを入れてから [OK] をクリックします。[Extra resources] では、ステップ 1 でアップロードした IP アドレスライブラリファイル ipv4.txt および ipv6.txt を選択する必要があります。登録する関数名は ipv4_ipv6_aton とします。

ステップ 5:MaxCompute UDF の呼び出しによる IP アドレスの所在地変換

  1. MaxCompute クライアントをインストールしてログオンします。

  2. SQL SELECT ステートメントを実行して MaxCompute UDF を呼び出し、IPv4 または IPv6 アドレスを所在地に変換します。

    コマンドの例は次のとおりです。

    • IPv4 アドレスの所在地への変換

      select ipv4_ipv6_aton('116.11.XX.XX');

      次のような結果が返されます。

      北海市,广西壮族自治区
    • IPv6 アドレスの所在地への変換

      select ipv4_ipv6_aton('2001:0250:080b:0:0:0:0:0');

      次のような結果が返されます。

      保定市,河北省