代码之家  ›  专栏  ›  技术社区  ›  Eddie

有没有JDK类来做HTML编码(而不是URL编码)?

  •  31
  • Eddie  · 技术社区  · 16 年前

    我当然熟悉 java.net.URLEncoder java.net.URLDecoder 类。但是,我只需要HTML样式的编码。(我不想要 ' ' 换成 '+' 等)。我不知道任何JDK内置类只做HTML编码。有吗?我知道其他选择(例如, Jakarta Commons Lang 'StringEscapeUtils' 但是我不想在我需要的项目中添加另一个外部依赖项。

    我希望最近的JDK(又称5或6)中添加了一些我不知道的功能。否则我必须自己滚。

    7 回复  |  直到 7 年前
        1
  •  45
  •   johnmcase    11 年前

    没有一个内置的JDK类来实现这一点,但它是雅加达公共语言库的一部分。

    String escaped = StringEscapeUtils.escapeHtml3(stringToEscape);
    String escaped = StringEscapeUtils.escapeHtml4(stringToEscape);
    

    退房 JavaDoc

    添加依赖项通常和将jar放到某个地方一样简单,而commons lang有很多有用的实用程序,因此通常值得将其放在板上。

        2
  •  13
  •   Rawton Evolekam    13 年前

    一个简单的方法是:

    public static String encodeHTML(String s)
    {
        StringBuffer out = new StringBuffer();
        for(int i=0; i<s.length(); i++)
        {
            char c = s.charAt(i);
            if(c > 127 || c=='"' || c=='<' || c=='>')
            {
               out.append("&#"+(int)c+";");
            }
            else
            {
                out.append(c);
            }
        }
        return out.toString();
    }
    

    来源: http://forums.thedailywtf.com/forums/p/2806/72054.aspx#72054

        3
  •  9
  •   Eddie    15 年前

    很显然,答案是“不”。不幸的是,我不得不做些事情, 不能 为它添加一个新的外部依赖——短期内。我同意所有人的观点,即使用通用语言是最好的长期解决方案。一旦我可以向项目中添加新的库,这就是我将要使用的方法。

    遗憾的是,Java API中没有使用这种常用的东西。

        4
  •  4
  •   peterh    8 年前

    我发现我所查看的所有现有解决方案(库)都存在以下一个或多个问题:

    • 在JavaDoc中,他们并没有确切地告诉你他们替换了什么。
    • 他们逃得太远了…这使得HTML更难阅读。
    • 他们没有记录 什么时候? 返回的值可以安全使用(是否可以安全地用于HTML实体?,对于HTML属性?等)
    • 它们没有针对速度进行优化。
    • 它们没有避免双重转义的功能(不要转义已经转义的内容)
    • 它们将单引号替换为 &apos; (错了!)

    除此之外,我还有一个问题,就是不能带进一个外部图书馆,至少不能没有一定数量的繁文缛节。

    所以,我自己滚。有罪的

    下面是它的外观,但最新版本始终可以在 this gist .

    /**
     * HTML string utilities
     */
    public class SafeHtml {
    
        /**
         * Escapes a string for use in an HTML entity or HTML attribute.
         * 
         * <p>
         * The returned value is always suitable for an HTML <i>entity</i> but only
         * suitable for an HTML <i>attribute</i> if the attribute value is inside
         * double quotes. In other words the method is not safe for use with HTML
         * attributes unless you put the value in double quotes like this:
         * <pre>
         *    &lt;div title="value-from-this-method" &gt; ....
         * </pre>
         * Putting attribute values in double quotes is always a good idea anyway.
         * 
         * <p>The following characters will be escaped:
         * <ul>
         *   <li>{@code &} (ampersand) -- replaced with {@code &amp;}</li>
         *   <li>{@code <} (less than) -- replaced with {@code &lt;}</li>
         *   <li>{@code >} (greater than) -- replaced with {@code &gt;}</li>
         *   <li>{@code "} (double quote) -- replaced with {@code &quot;}</li>
         *   <li>{@code '} (single quote) -- replaced with {@code &#39;}</li>
         *   <li>{@code /} (forward slash) -- replaced with {@code &#47;}</li>
         * </ul>
         * It is not necessary to escape more than this as long as the HTML page
         * <a href="https://en.wikipedia.org/wiki/Character_encodings_in_HTML">uses
         * a Unicode encoding</a>. (Most web pages uses UTF-8 which is also the HTML5
         * recommendation.). Escaping more than this makes the HTML much less readable.
         * 
         * @param s the string to make HTML safe
         * @param avoidDoubleEscape avoid double escaping, which means for example not 
         *     escaping {@code &lt;} one more time. Any sequence {@code &....;}, as explained in
         *     {@link #isHtmlCharEntityRef(java.lang.String, int) isHtmlCharEntityRef()}, will not be escaped.
         * 
         * @return a HTML safe string 
         */
        public static String htmlEscape(String s, boolean avoidDoubleEscape) {
            if (s == null || s.length() == 0) {
                return s;
            }
            StringBuilder sb = new StringBuilder(s.length()+16);
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                switch (c) {
                    case '&':
                        // Avoid double escaping if already escaped
                        if (avoidDoubleEscape && (isHtmlCharEntityRef(s, i))) {
                            sb.append('&');
                        } else {
                            sb.append("&amp;");
                        }
                        break;
                    case '<':
                        sb.append("&lt;");
                        break;
                    case '>':
                        sb.append("&gt;");
                        break;
                    case '"':
                        sb.append("&quot;"); 
                        break;
                    case '\'':
                        sb.append("&#39;"); 
                        break;
                    case '/':
                        sb.append("&#47;"); 
                        break;
                    default:
                        sb.append(c);
                }
            }
            return sb.toString();
      }
    
      /**
       * Checks if the value at {@code index} is a HTML entity reference. This
       * means any of :
       * <ul>
       *   <li>{@code &amp;} or {@code &lt;} or {@code &gt;} or {@code &quot;} </li>
       *   <li>A value of the form {@code &#dddd;} where {@code dddd} is a decimal value</li>
       *   <li>A value of the form {@code &#xhhhh;} where {@code hhhh} is a hexadecimal value</li>
       * </ul>
       * @param str the string to test for HTML entity reference.
       * @param index position of the {@code '&'} in {@code str}
       * @return 
       */
      public static boolean isHtmlCharEntityRef(String str, int index)  {
          if (str.charAt(index) != '&') {
              return false;
          }
          int indexOfSemicolon = str.indexOf(';', index + 1);
          if (indexOfSemicolon == -1) { // is there a semicolon sometime later ?
              return false;
          }
          if (!(indexOfSemicolon > (index + 2))) {   // is the string actually long enough
              return false;
          }
          if (followingCharsAre(str, index, "amp;")
                  || followingCharsAre(str, index, "lt;")
                  || followingCharsAre(str, index, "gt;")
                  || followingCharsAre(str, index, "quot;")) {
              return true;
          }
          if (str.charAt(index+1) == '#') {
              if (str.charAt(index+2) == 'x' || str.charAt(index+2) == 'X') {
                  // It's presumably a hex value
                  if (str.charAt(index+3) == ';') {
                      return false;
                  }
                  for (int i = index+3; i < indexOfSemicolon; i++) {
                      char c = str.charAt(i);
                      if (c >= 48 && c <=57) {  // 0 -- 9
                          continue;
                      }
                      if (c >= 65 && c <=70) {   // A -- F
                          continue;
                      }
                      if (c >= 97 && c <=102) {   // a -- f
                          continue;
                      }
                      return false;  
                  }
                  return true;   // yes, the value is a hex string
              } else {
                  // It's presumably a decimal value
                  for (int i = index+2; i < indexOfSemicolon; i++) {
                      char c = str.charAt(i);
                      if (c >= 48 && c <=57) {  // 0 -- 9
                          continue;
                      }
                      return false;
                  }
                  return true; // yes, the value is decimal
              }
          }
          return false;
      } 
    
    
      /**
       * Tests if the chars following position <code>startIndex</code> in string
       * <code>str</code> are that of <code>nextChars</code>.
       * 
       * <p>Optimized for speed. Otherwise this method would be exactly equal to
       * {@code (str.indexOf(nextChars, startIndex+1) == (startIndex+1))}.
       *
       * @param str
       * @param startIndex
       * @param nextChars
       * @return 
       */  
      private static boolean followingCharsAre(String str, int startIndex, String nextChars)  {
          if ((startIndex + nextChars.length()) < str.length()) {
              for(int i = 0; i < nextChars.length(); i++) {
                  if ( nextChars.charAt(i) != str.charAt(startIndex+i+1)) {
                      return false;
                  }
              }
              return true;
          } else {
              return false;
          }
      }
    }
    

    TODO:保留连续空白。

        5
  •  1
  •   bitboxer    16 年前

    请不要自己滚。使用Jakarta Commons Lang。它经过测试并证明有效。在必须的时候才写代码。”这里没有发明“或“不是另一种依赖”不是决定选择/写什么的一个很好的基础。

        6
  •  0
  •   simon    16 年前

    不。我建议您使用您提到的StringEscapeUtils,或者例如jTiddyUtils( http://jtidy.sourceforge.net/multiproject/jtidyservlet/apidocs/org/w3c/tidy/servlet/util/HTMLEncode.html )

        7
  •  -1
  •   Sachin Kokate    7 年前

    我建议使用org.springframework.web.util.htmlutils.htmlescape(字符串输入)

    也许这会有帮助。