【问题标题】:What is the fastest way to get the domain/host name from a URL?从 URL 获取域名/主机名的最快方法是什么?
【发布时间】:2011-06-17 02:21:59
【问题描述】:

我需要浏览大量的字符串 url 并从中提取域名。

例如:

http://www.stackoverflow.com/questions 将提取 www.stackoverflow.com

我最初使用的是new URL(theUrlString).getHost(),但 URL 对象初始化为该过程增加了很多时间,并且似乎不需要。

是否有更快的方法来提取同样可靠的主机名?

谢谢

编辑:我的错误,是的 www。将包含在上面的域名示例中。此外,这些网址可能是 http 或 https

【问题讨论】:

  • 您可以使用正则表达式或一些简单的字符串操作来提取它,即删除前导http://https://,然后将所有内容提取到第一个/: (端口 - 不确定你是否想要这个)。但是我不确定这是否涵盖所有情况(因此是评论而不是答案)

标签: java url dns


【解决方案1】:

如果你想处理https等,我建议你这样做:

int slashslash = url.indexOf("//") + 2;
domain = url.substring(slashslash, url.indexOf('/', slashslash));

请注意,这包括www 部分(就像URL.getHost() 会做的那样),它实际上是域名的一部分。

通过 cmets 请求编辑

这里有两种可能有用的方法:

/**
 * Will take a url such as http://www.stackoverflow.com and return www.stackoverflow.com
 * 
 * @param url
 * @return
 */
public static String getHost(String url){
    if(url == null || url.length() == 0)
        return "";

    int doubleslash = url.indexOf("//");
    if(doubleslash == -1)
        doubleslash = 0;
    else
        doubleslash += 2;

    int end = url.indexOf('/', doubleslash);
    end = end >= 0 ? end : url.length();

    int port = url.indexOf(':', doubleslash);
    end = (port > 0 && port < end) ? port : end;

    return url.substring(doubleslash, end);
}


/**  Based on : http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/android/webkit/CookieManager.java#CookieManager.getBaseDomain%28java.lang.String%29
 * Get the base domain for a given host or url. E.g. mail.google.com will return google.com
 * @param host 
 * @return 
 */
public static String getBaseDomain(String url) {
    String host = getHost(url);

    int startIndex = 0;
    int nextIndex = host.indexOf('.');
    int lastIndex = host.lastIndexOf('.');
    while (nextIndex < lastIndex) {
        startIndex = nextIndex + 1;
        nextIndex = host.indexOf('.', startIndex);
    }
    if (startIndex > 0) {
        return host.substring(startIndex);
    } else {
        return host;
    }
}

【讨论】:

  • 这几乎是我使用的新 URL().getHost() 方法的 12 倍,而且看起来也同样有效。在将其标记为已回答之前,我将尝试获得一个好的正则表达式模式并运行更多基准测试,但这是一个很好的改进!
  • 另外,我不得不稍微更改您的代码以添加一个检查以防尾随 / 不存在。
  • 啊,是的,好点子!随意编辑我的答案并包括您的改进!
  • littleFluffyKitty,如果你能更新他的答案就好了。
  • @aioobe getBaseDomain 在 amazon.co.uk 等网址上失败
【解决方案2】:

您需要非常小心地实施“快速”的方式来取消选择 URL。 URL 中有很多潜在的可变性,可能会导致“快速”方法失败。例如:

  • 方案(协议)部分可以用大小写字母任意组合;例如“http”、“Http”和“HTTP”是等价的。

  • 权限部分可以选择包括用户名和/或端口号,如“http://you@example.com:8080/index.html”。

  • 由于 DNS 不区分大小写,因此 URL 的主机名部分也(有效地)不区分大小写。

  • 对 URL 的方案或权限组件中的未保留字符进行 % 编码是合法的(尽管非常不规则)。在匹配(或剥离)方案或解释主机名时,您需要考虑到这一点。带有 %-encoded 字符的主机名被定义为等同于带有 %-encoded 序列解码的主机名。

现在,如果您可以完全控制生成要剥离的 URL 的过程,您可能可以忽略这些细节。但是,如果它们是从文档或网页中获取的,或者是由人类输入的,那么建议您考虑一下如果您的代码遇到“不寻常”的 URL 可能会发生什么。


如果您关心的是构建 URL 对象所花费的时间,请考虑改用 URI 对象。除了其他好处之外,URI 对象不会尝试对主机名部分进行 DNS 查找。

【讨论】:

  • 是否有这样可靠的方法不需要为每次转换创建新的 URL 对象?
  • @littleFluffyKitty - 是的...改用 URI 对象。
  • 感谢您的回复,虽然我很好奇是什么让您在两年后回到这里回复;)
  • 有人对答案投了赞成票,我回头看了看,发现我之前的评论没有抓住重点。
【解决方案3】:

我写了一个方法(见下文),它提取一个 url 的域名并使用简单的字符串匹配。它实际上做的是提取第一个"://"(或索引0,如果不包含"://")和第一个后续"/"(或索引String.length(),如果没有后续"/")之间的位。剩余的,前面的"www(_)*." 位被切掉。我相信在某些情况下这还不够好,但在大多数情况下应该足够好!

我读到 here java.net.URI 类可以做到这一点(并且比 java.net.URL 类更受欢迎)但我遇到了 URI 类的问题。值得注意的是,如果 url 不包含该方案,URI.getHost() 会给出一个空值,即"http(s)" 位。

/**
 * Extracts the domain name from {@code url}
 * by means of String manipulation
 * rather than using the {@link URI} or {@link URL} class.
 *
 * @param url is non-null.
 * @return the domain name within {@code url}.
 */
public String getUrlDomainName(String url) {
  String domainName = new String(url);

  int index = domainName.indexOf("://");

  if (index != -1) {
    // keep everything after the "://"
    domainName = domainName.substring(index + 3);
  }

  index = domainName.indexOf('/');

  if (index != -1) {
    // keep everything before the '/'
    domainName = domainName.substring(0, index);
  }

  // check for and remove a preceding 'www'
  // followed by any sequence of characters (non-greedy)
  // followed by a '.'
  // from the beginning of the string
  domainName = domainName.replaceFirst("^www.*?\\.", "");

  return domainName;
}

【讨论】:

    【解决方案4】:

    尝试方法:该类中的getDomainFromUrl()

    package com.visc.mobilesecurity.childrencare.utils;
    
    import android.content.Context;
    
    import com.visc.mobilesecurity.antitheft.backwardcompatibility.FroyoSupport;
    import com.visc.mobilesecurity.antitheft.util.AntiTheftUtils;
    import com.visc.mobilesecurity.constant.Key;
    import com.visc.mobilesecurity.util.Prefs;
    
    import org.json.JSONObject;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    
    /**
     * Created by thongnv12 on 3/9/2018.
     */
    
    public class ChildcareUtils {
    
        public static final String[] NATION_DOMAIN = {"af", "ax", "al", "dz", "as", "ad", "ao", "ai", "aq", "ag", "ar", "am", "aw", "ac", "au", "at", "az", "bs", "bh", "bd", "bb", "eus",
                "by", "be", "bz", "bj", "bm", "bt", "bo", "bq", "ba", "bw", "bv", "br", "io", "vg", "bn", "bg", "bf", "mm", "bi", "kh", "cm", "ca", "cv", "cat", "ky", "cf", "td", "cl",
                "cn", "cx", "cc", "co", "km", "cd", "cg", "ck", "cr", "ci", "hr", "cu", "cw", "cy", "cz", "dk", "dj", "dm", "do", "tl", "ec", "eg", "sv", "gq", "er", "ee", "et", "eu",
                "fk", "fo", "fm", "fj", "fi", "fr", "gf", "pf", "tf", "ga", "gal", "gm", "ps", "ge", "de", "gh", "gi", "gr", "gl", "gd", "gp", "gu", "gt", "gg", "gn", "gw", "gy", "ht",
                "hm", "hn", "hk", "hu", "is", "in", "id", "ir", "iq", "ie", "im", "il", "it", "jm", "jp", "je", "jo", "kz", "ke", "ki", "kw", "kg", "la", "lv", "lb", "ls", "lr", "ly",
                "li", "lt", "lu", "mo", "mk", "mg", "mw", "my", "mv", "ml", "mt", "mh", "mq", "mr", "mu", "yt", "mx", "md", "mc", "mn", "me", "ms", "ma", "mz", "mm", "na", "nr", "np",
                "nl", "nc", "nz", "ni", "ne", "ng", "nu", "nf", "kp", "mp", "no", "om", "pk", "pw", "ps", "pa", "pg", "py", "pe", "ph", "pn", "pl", "pt", "pr", "qa", "ro", "ru", "rw",
                "re", "bq", "bl", "sh", "kn", "lc", "mf", "fr", "pm", "vc", "ws", "sm", "st", "sa", "sn", "rs", "sc", "sl", "sg", "bq", "sx", "sk", "si", "sb", "so", "so", "za", "gs",
                "kr", "ss", "es", "lk", "sd", "sr", "sj", "sz", "se", "ch", "sy", "tw", "tj", "tz", "th", "tg", "tk", "to", "tt", "tn", "tr", "tm", "tc", "tv", "ug", "ua", "ae", "uk",
                "us", "vi", "uy", "uz", "vu", "va", "ve", "vn", "wf", "eh", "zm", "zw"};
    
    
        public static boolean isInNationString(String str) {
            for (int index = 0; index < NATION_DOMAIN.length; index++) {
                if (NATION_DOMAIN[index].equals(str)) {
                    return true;
                }
            }
            return false;
        }
    
    
        public static String getDomainFromUrl(String urlStr) {
            try {
                String result = null;
    //            URL url = new URL(urlStr);
    //            result = url.getHost();
    //            return result;
    //
                // for test
                // check dau cach
                if (urlStr.contains(" ")) {
                    return null;
                }
                // replace
                urlStr = urlStr.replace("https://", "");
                urlStr = urlStr.replace("http://", "");
                urlStr = urlStr.replace("www.", "");
                //
                String[] splitStr = urlStr.split("/");
    
                String domainFull = splitStr[0];
    
                String[] splitDot = domainFull.split("\\.");
    
                if (splitDot.length < 2) {
                    return null;
                }
    
                String nationStr = splitDot[splitDot.length - 1];
    
                if (isInNationString(nationStr)) {
                    if (splitDot.length < 4) {
                        result = domainFull;
                    } else {
                        StringBuilder strResult = new StringBuilder();
                        int lengthDot = splitDot.length;
                        strResult.append(splitDot[lengthDot - 3]).append(".");
                        strResult.append(splitDot[lengthDot - 2]).append(".");
                        strResult.append(splitDot[lengthDot - 1]);
                        result = strResult.toString();
                    }
    
                } else {
                    if (splitDot.length < 3) {
                        result = domainFull;
                    } else {
                        StringBuilder strResult = new StringBuilder();
                        int lengthDot = splitDot.length;
                        strResult.append(splitDot[lengthDot - 2]).append(".");
                        strResult.append(splitDot[lengthDot - 1]);
                        result = strResult.toString();
                    }
                }
                return result;
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
    
        }
    }
    

    【讨论】:

      【解决方案5】:

      只有另一种获取主机的方法

      private String getHostName(String hostname) {
          // to provide faultproof result, check if not null then return only hostname, without www.
          if (hostname != null) {
              return hostname.startsWith("www.") ? hostname.substring(4) : getHostNameDFExt(hostname);
          }
          return hostname;
      }
      
      private String getHostNameDFExt(String hostname) {
      
          int substringIndex = 0;
          for (char character : hostname.toCharArray()) {
              substringIndex++;
              if (character == '.') {
                  break;
              }
          }
      
          return hostname.substring(substringIndex);
      
      }
      

      现在我们必须在从 URL 中提取后在函数中传递主机名

      URL url = new URL("https://www.facebook.com/");
      String hostname = getHostName(ur.getHost());
      
      Toast.makeText(this, hostname, Toast.LENGTH_SHORT).show();
      

      输出将是:“facebook.com

      【讨论】:

      • 为什么需要getHostNameDFExt?只需将 hostname 对称地返回到 hostname.startsWith("www.") true 案例中所需的内容即可。
      【解决方案6】:

      你可以写一个正则表达式吗? http:// 总是一样的,然后匹配所有内容,直到你得到第一个'/'。

      【讨论】:

      • 正则表达式不是可能是最快的方法。
      • 可能会比构建 url 更快。你为什么这么认为? '直觉'或者你有什么参考吗?
      • reg-exp 引擎生成一个 FSM 并更新每个字符的某些状态。我怀疑它是否更快,但如果您对其进行基准测试,请告诉我们您的发现。
      【解决方案7】:

      假设它们都是格式正确的 URL,但你不知道它们是否会是 http://、https:// 等。

      int start = theUrlString.indexOf('/'); int start = theUrlString.indexOf('/', start+1); int end = theUrlString.indexOf('/', start+1); String domain = theUrlString.subString(start, end);

      【讨论】:

      • 您真正想要start 的哪个值? :-)
      【解决方案8】:

      您可以尝试使用正则表达式。

      http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html

      这里有一个关于Java中用正则表达式提取域名的问题:

      Regular expression to retrieve domain.tld

      【讨论】:

        猜你喜欢
        • 2022-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-26
        • 2014-10-24
        • 1970-01-01
        • 2010-10-23
        相关资源
        最近更新 更多