001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3; 018 019import java.io.IOException; 020import java.io.Writer; 021 022import org.apache.commons.lang3.text.translate.AggregateTranslator; 023import org.apache.commons.lang3.text.translate.CharSequenceTranslator; 024import org.apache.commons.lang3.text.translate.EntityArrays; 025import org.apache.commons.lang3.text.translate.JavaUnicodeEscaper; 026import org.apache.commons.lang3.text.translate.LookupTranslator; 027import org.apache.commons.lang3.text.translate.NumericEntityUnescaper; 028import org.apache.commons.lang3.text.translate.OctalUnescaper; 029import org.apache.commons.lang3.text.translate.UnicodeUnescaper; 030 031/** 032 * <p>Escapes and unescapes {@code String}s for 033 * Java, Java Script, HTML and XML.</p> 034 * 035 * <p>#ThreadSafe#</p> 036 * @since 2.0 037 * @version $Id: StringEscapeUtils.java 1467206 2013-04-12 08:39:02Z bayard $ 038 */ 039public class StringEscapeUtils { 040 041 /* ESCAPE TRANSLATORS */ 042 043 /** 044 * Translator object for escaping Java. 045 * 046 * While {@link #escapeJava(String)} is the expected method of use, this 047 * object allows the Java escaping functionality to be used 048 * as the foundation for a custom translator. 049 * 050 * @since 3.0 051 */ 052 public static final CharSequenceTranslator ESCAPE_JAVA = 053 new LookupTranslator( 054 new String[][] { 055 {"\"", "\\\""}, 056 {"\\", "\\\\"}, 057 }).with( 058 new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()) 059 ).with( 060 JavaUnicodeEscaper.outsideOf(32, 0x7f) 061 ); 062 063 /** 064 * Translator object for escaping EcmaScript/JavaScript. 065 * 066 * While {@link #escapeEcmaScript(String)} is the expected method of use, this 067 * object allows the EcmaScript escaping functionality to be used 068 * as the foundation for a custom translator. 069 * 070 * @since 3.0 071 */ 072 public static final CharSequenceTranslator ESCAPE_ECMASCRIPT = 073 new AggregateTranslator( 074 new LookupTranslator( 075 new String[][] { 076 {"'", "\\'"}, 077 {"\"", "\\\""}, 078 {"\\", "\\\\"}, 079 {"/", "\\/"} 080 }), 081 new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()), 082 JavaUnicodeEscaper.outsideOf(32, 0x7f) 083 ); 084 085 /** 086 * Translator object for escaping Json. 087 * 088 * While {@link #escapeJson(String)} is the expected method of use, this 089 * object allows the Json escaping functionality to be used 090 * as the foundation for a custom translator. 091 * 092 * @since 3.2 093 */ 094 public static final CharSequenceTranslator ESCAPE_JSON = 095 new AggregateTranslator( 096 new LookupTranslator( 097 new String[][] { 098 {"\"", "\\\""}, 099 {"\\", "\\\\"}, 100 {"/", "\\/"} 101 }), 102 new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()), 103 JavaUnicodeEscaper.outsideOf(32, 0x7f) 104 ); 105 106 /** 107 * Translator object for escaping XML. 108 * 109 * While {@link #escapeXml(String)} is the expected method of use, this 110 * object allows the XML escaping functionality to be used 111 * as the foundation for a custom translator. 112 * 113 * @since 3.0 114 */ 115 public static final CharSequenceTranslator ESCAPE_XML = 116 new AggregateTranslator( 117 new LookupTranslator(EntityArrays.BASIC_ESCAPE()), 118 new LookupTranslator(EntityArrays.APOS_ESCAPE()) 119 ); 120 121 /** 122 * Translator object for escaping HTML version 3.0. 123 * 124 * While {@link #escapeHtml3(String)} is the expected method of use, this 125 * object allows the HTML escaping functionality to be used 126 * as the foundation for a custom translator. 127 * 128 * @since 3.0 129 */ 130 public static final CharSequenceTranslator ESCAPE_HTML3 = 131 new AggregateTranslator( 132 new LookupTranslator(EntityArrays.BASIC_ESCAPE()), 133 new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()) 134 ); 135 136 /** 137 * Translator object for escaping HTML version 4.0. 138 * 139 * While {@link #escapeHtml4(String)} is the expected method of use, this 140 * object allows the HTML escaping functionality to be used 141 * as the foundation for a custom translator. 142 * 143 * @since 3.0 144 */ 145 public static final CharSequenceTranslator ESCAPE_HTML4 = 146 new AggregateTranslator( 147 new LookupTranslator(EntityArrays.BASIC_ESCAPE()), 148 new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()), 149 new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE()) 150 ); 151 152 /** 153 * Translator object for escaping individual Comma Separated Values. 154 * 155 * While {@link #escapeCsv(String)} is the expected method of use, this 156 * object allows the CSV escaping functionality to be used 157 * as the foundation for a custom translator. 158 * 159 * @since 3.0 160 */ 161 public static final CharSequenceTranslator ESCAPE_CSV = new CsvEscaper(); 162 163 // TODO: Create a parent class - 'SinglePassTranslator' ? 164 // It would handle the index checking + length returning, 165 // and could also have an optimization check method. 166 static class CsvEscaper extends CharSequenceTranslator { 167 168 private static final char CSV_DELIMITER = ','; 169 private static final char CSV_QUOTE = '"'; 170 private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE); 171 private static final char[] CSV_SEARCH_CHARS = 172 new char[] {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF}; 173 174 @Override 175 public int translate(final CharSequence input, final int index, final Writer out) throws IOException { 176 177 if(index != 0) { 178 throw new IllegalStateException("CsvEscaper should never reach the [1] index"); 179 } 180 181 if (StringUtils.containsNone(input.toString(), CSV_SEARCH_CHARS)) { 182 out.write(input.toString()); 183 } else { 184 out.write(CSV_QUOTE); 185 out.write(StringUtils.replace(input.toString(), CSV_QUOTE_STR, CSV_QUOTE_STR + CSV_QUOTE_STR)); 186 out.write(CSV_QUOTE); 187 } 188 return input.length(); 189 } 190 } 191 192 /* UNESCAPE TRANSLATORS */ 193 194 /** 195 * Translator object for unescaping escaped Java. 196 * 197 * While {@link #unescapeJava(String)} is the expected method of use, this 198 * object allows the Java unescaping functionality to be used 199 * as the foundation for a custom translator. 200 * 201 * @since 3.0 202 */ 203 // TODO: throw "illegal character: \92" as an Exception if a \ on the end of the Java (as per the compiler)? 204 public static final CharSequenceTranslator UNESCAPE_JAVA = 205 new AggregateTranslator( 206 new OctalUnescaper(), // .between('\1', '\377'), 207 new UnicodeUnescaper(), 208 new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_UNESCAPE()), 209 new LookupTranslator( 210 new String[][] { 211 {"\\\\", "\\"}, 212 {"\\\"", "\""}, 213 {"\\'", "'"}, 214 {"\\", ""} 215 }) 216 ); 217 218 /** 219 * Translator object for unescaping escaped EcmaScript. 220 * 221 * While {@link #unescapeEcmaScript(String)} is the expected method of use, this 222 * object allows the EcmaScript unescaping functionality to be used 223 * as the foundation for a custom translator. 224 * 225 * @since 3.0 226 */ 227 public static final CharSequenceTranslator UNESCAPE_ECMASCRIPT = UNESCAPE_JAVA; 228 229 /** 230 * Translator object for unescaping escaped Json. 231 * 232 * While {@link #unescapeJson(String)} is the expected method of use, this 233 * object allows the Json unescaping functionality to be used 234 * as the foundation for a custom translator. 235 * 236 * @since 3.2 237 */ 238 public static final CharSequenceTranslator UNESCAPE_JSON = UNESCAPE_JAVA; 239 240 /** 241 * Translator object for unescaping escaped HTML 3.0. 242 * 243 * While {@link #unescapeHtml3(String)} is the expected method of use, this 244 * object allows the HTML unescaping functionality to be used 245 * as the foundation for a custom translator. 246 * 247 * @since 3.0 248 */ 249 public static final CharSequenceTranslator UNESCAPE_HTML3 = 250 new AggregateTranslator( 251 new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), 252 new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()), 253 new NumericEntityUnescaper() 254 ); 255 256 /** 257 * Translator object for unescaping escaped HTML 4.0. 258 * 259 * While {@link #unescapeHtml4(String)} is the expected method of use, this 260 * object allows the HTML unescaping functionality to be used 261 * as the foundation for a custom translator. 262 * 263 * @since 3.0 264 */ 265 public static final CharSequenceTranslator UNESCAPE_HTML4 = 266 new AggregateTranslator( 267 new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), 268 new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()), 269 new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE()), 270 new NumericEntityUnescaper() 271 ); 272 273 /** 274 * Translator object for unescaping escaped XML. 275 * 276 * While {@link #unescapeXml(String)} is the expected method of use, this 277 * object allows the XML unescaping functionality to be used 278 * as the foundation for a custom translator. 279 * 280 * @since 3.0 281 */ 282 public static final CharSequenceTranslator UNESCAPE_XML = 283 new AggregateTranslator( 284 new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), 285 new LookupTranslator(EntityArrays.APOS_UNESCAPE()), 286 new NumericEntityUnescaper() 287 ); 288 289 /** 290 * Translator object for unescaping escaped Comma Separated Value entries. 291 * 292 * While {@link #unescapeCsv(String)} is the expected method of use, this 293 * object allows the CSV unescaping functionality to be used 294 * as the foundation for a custom translator. 295 * 296 * @since 3.0 297 */ 298 public static final CharSequenceTranslator UNESCAPE_CSV = new CsvUnescaper(); 299 300 static class CsvUnescaper extends CharSequenceTranslator { 301 302 private static final char CSV_DELIMITER = ','; 303 private static final char CSV_QUOTE = '"'; 304 private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE); 305 private static final char[] CSV_SEARCH_CHARS = 306 new char[] {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF}; 307 308 @Override 309 public int translate(final CharSequence input, final int index, final Writer out) throws IOException { 310 311 if(index != 0) { 312 throw new IllegalStateException("CsvUnescaper should never reach the [1] index"); 313 } 314 315 if ( input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE ) { 316 out.write(input.toString()); 317 return input.length(); 318 } 319 320 // strip quotes 321 final String quoteless = input.subSequence(1, input.length() - 1).toString(); 322 323 if ( StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS) ) { 324 // deal with escaped quotes; ie) "" 325 out.write(StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR)); 326 } else { 327 out.write(input.toString()); 328 } 329 return input.length(); 330 } 331 } 332 333 /* Helper functions */ 334 335 /** 336 * <p>{@code StringEscapeUtils} instances should NOT be constructed in 337 * standard programming.</p> 338 * 339 * <p>Instead, the class should be used as: 340 * <pre>StringEscapeUtils.escapeJava("foo");</pre></p> 341 * 342 * <p>This constructor is public to permit tools that require a JavaBean 343 * instance to operate.</p> 344 */ 345 public StringEscapeUtils() { 346 super(); 347 } 348 349 // Java and JavaScript 350 //-------------------------------------------------------------------------- 351 /** 352 * <p>Escapes the characters in a {@code String} using Java String rules.</p> 353 * 354 * <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p> 355 * 356 * <p>So a tab becomes the characters {@code '\\'} and 357 * {@code 't'}.</p> 358 * 359 * <p>The only difference between Java strings and JavaScript strings 360 * is that in JavaScript, a single quote and forward-slash (/) are escaped.</p> 361 * 362 * <p>Example: 363 * <pre> 364 * input string: He didn't say, "Stop!" 365 * output string: He didn't say, \"Stop!\" 366 * </pre> 367 * </p> 368 * 369 * @param input String to escape values in, may be null 370 * @return String with escaped values, {@code null} if null string input 371 */ 372 public static final String escapeJava(final String input) { 373 return ESCAPE_JAVA.translate(input); 374 } 375 376 /** 377 * <p>Escapes the characters in a {@code String} using EcmaScript String rules.</p> 378 * <p>Escapes any values it finds into their EcmaScript String form. 379 * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p> 380 * 381 * <p>So a tab becomes the characters {@code '\\'} and 382 * {@code 't'}.</p> 383 * 384 * <p>The only difference between Java strings and EcmaScript strings 385 * is that in EcmaScript, a single quote and forward-slash (/) are escaped.</p> 386 * 387 * <p>Note that EcmaScript is best known by the JavaScript and ActionScript dialects. </p> 388 * 389 * <p>Example: 390 * <pre> 391 * input string: He didn't say, "Stop!" 392 * output string: He didn\'t say, \"Stop!\" 393 * </pre> 394 * </p> 395 * 396 * @param input String to escape values in, may be null 397 * @return String with escaped values, {@code null} if null string input 398 * 399 * @since 3.0 400 */ 401 public static final String escapeEcmaScript(final String input) { 402 return ESCAPE_ECMASCRIPT.translate(input); 403 } 404 405 /** 406 * <p>Escapes the characters in a {@code String} using Json String rules.</p> 407 * <p>Escapes any values it finds into their Json String form. 408 * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p> 409 * 410 * <p>So a tab becomes the characters {@code '\\'} and 411 * {@code 't'}.</p> 412 * 413 * <p>The only difference between Java strings and Json strings 414 * is that in Json, forward-slash (/) is escaped.</p> 415 * 416 * <p>See http://www.ietf.org/rfc/rfc4627.txt for further details. </p> 417 * 418 * <p>Example: 419 * <pre> 420 * input string: He didn't say, "Stop!" 421 * output string: He didn't say, \"Stop!\" 422 * </pre> 423 * </p> 424 * 425 * @param input String to escape values in, may be null 426 * @return String with escaped values, {@code null} if null string input 427 * 428 * @since 3.2 429 */ 430 public static final String escapeJson(final String input) { 431 return ESCAPE_JSON.translate(input); 432 } 433 434 /** 435 * <p>Unescapes any Java literals found in the {@code String}. 436 * For example, it will turn a sequence of {@code '\'} and 437 * {@code 'n'} into a newline character, unless the {@code '\'} 438 * is preceded by another {@code '\'}.</p> 439 * 440 * @param input the {@code String} to unescape, may be null 441 * @return a new unescaped {@code String}, {@code null} if null string input 442 */ 443 public static final String unescapeJava(final String input) { 444 return UNESCAPE_JAVA.translate(input); 445 } 446 447 /** 448 * <p>Unescapes any EcmaScript literals found in the {@code String}.</p> 449 * 450 * <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'} 451 * into a newline character, unless the {@code '\'} is preceded by another 452 * {@code '\'}.</p> 453 * 454 * @see #unescapeJava(String) 455 * @param input the {@code String} to unescape, may be null 456 * @return A new unescaped {@code String}, {@code null} if null string input 457 * 458 * @since 3.0 459 */ 460 public static final String unescapeEcmaScript(final String input) { 461 return UNESCAPE_ECMASCRIPT.translate(input); 462 } 463 464 /** 465 * <p>Unescapes any Json literals found in the {@code String}.</p> 466 * 467 * <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'} 468 * into a newline character, unless the {@code '\'} is preceded by another 469 * {@code '\'}.</p> 470 * 471 * @see #unescapeJava(String) 472 * @param input the {@code String} to unescape, may be null 473 * @return A new unescaped {@code String}, {@code null} if null string input 474 * 475 * @since 3.2 476 */ 477 public static final String unescapeJson(final String input) { 478 return UNESCAPE_JSON.translate(input); 479 } 480 481 // HTML and XML 482 //-------------------------------------------------------------------------- 483 /** 484 * <p>Escapes the characters in a {@code String} using HTML entities.</p> 485 * 486 * <p> 487 * For example: 488 * </p> 489 * <p><code>"bread" & "butter"</code></p> 490 * becomes: 491 * <p> 492 * <code>&quot;bread&quot; &amp; &quot;butter&quot;</code>. 493 * </p> 494 * 495 * <p>Supports all known HTML 4.0 entities, including funky accents. 496 * Note that the commonly used apostrophe escape character (&apos;) 497 * is not a legal entity and so is not supported). </p> 498 * 499 * @param input the {@code String} to escape, may be null 500 * @return a new escaped {@code String}, {@code null} if null string input 501 * 502 * @see <a href="http://hotwired.lycos.com/webmonkey/reference/special_characters/">ISO Entities</a> 503 * @see <a href="http://www.w3.org/TR/REC-html32#latin1">HTML 3.2 Character Entities for ISO Latin-1</a> 504 * @see <a href="http://www.w3.org/TR/REC-html40/sgml/entities.html">HTML 4.0 Character entity references</a> 505 * @see <a href="http://www.w3.org/TR/html401/charset.html#h-5.3">HTML 4.01 Character References</a> 506 * @see <a href="http://www.w3.org/TR/html401/charset.html#code-position">HTML 4.01 Code positions</a> 507 * 508 * @since 3.0 509 */ 510 public static final String escapeHtml4(final String input) { 511 return ESCAPE_HTML4.translate(input); 512 } 513 514 /** 515 * <p>Escapes the characters in a {@code String} using HTML entities.</p> 516 * <p>Supports only the HTML 3.0 entities. </p> 517 * 518 * @param input the {@code String} to escape, may be null 519 * @return a new escaped {@code String}, {@code null} if null string input 520 * 521 * @since 3.0 522 */ 523 public static final String escapeHtml3(final String input) { 524 return ESCAPE_HTML3.translate(input); 525 } 526 527 //----------------------------------------------------------------------- 528 /** 529 * <p>Unescapes a string containing entity escapes to a string 530 * containing the actual Unicode characters corresponding to the 531 * escapes. Supports HTML 4.0 entities.</p> 532 * 533 * <p>For example, the string "&lt;Fran&ccedil;ais&gt;" 534 * will become "<Français>"</p> 535 * 536 * <p>If an entity is unrecognized, it is left alone, and inserted 537 * verbatim into the result string. e.g. "&gt;&zzzz;x" will 538 * become ">&zzzz;x".</p> 539 * 540 * @param input the {@code String} to unescape, may be null 541 * @return a new unescaped {@code String}, {@code null} if null string input 542 * 543 * @since 3.0 544 */ 545 public static final String unescapeHtml4(final String input) { 546 return UNESCAPE_HTML4.translate(input); 547 } 548 549 /** 550 * <p>Unescapes a string containing entity escapes to a string 551 * containing the actual Unicode characters corresponding to the 552 * escapes. Supports only HTML 3.0 entities.</p> 553 * 554 * @param input the {@code String} to unescape, may be null 555 * @return a new unescaped {@code String}, {@code null} if null string input 556 * 557 * @since 3.0 558 */ 559 public static final String unescapeHtml3(final String input) { 560 return UNESCAPE_HTML3.translate(input); 561 } 562 563 //----------------------------------------------------------------------- 564 /** 565 * <p>Escapes the characters in a {@code String} using XML entities.</p> 566 * 567 * <p>For example: <tt>"bread" & "butter"</tt> => 568 * <tt>&quot;bread&quot; &amp; &quot;butter&quot;</tt>. 569 * </p> 570 * 571 * <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos). 572 * Does not support DTDs or external entities.</p> 573 * 574 * <p>Note that Unicode characters greater than 0x7f are as of 3.0, no longer 575 * escaped. If you still wish this functionality, you can achieve it 576 * via the following: 577 * {@code StringEscapeUtils.ESCAPE_XML.with( NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE) );}</p> 578 * 579 * @param input the {@code String} to escape, may be null 580 * @return a new escaped {@code String}, {@code null} if null string input 581 * @see #unescapeXml(java.lang.String) 582 */ 583 public static final String escapeXml(final String input) { 584 return ESCAPE_XML.translate(input); 585 } 586 587 588 //----------------------------------------------------------------------- 589 /** 590 * <p>Unescapes a string containing XML entity escapes to a string 591 * containing the actual Unicode characters corresponding to the 592 * escapes.</p> 593 * 594 * <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos). 595 * Does not support DTDs or external entities.</p> 596 * 597 * <p>Note that numerical \\u Unicode codes are unescaped to their respective 598 * Unicode characters. This may change in future releases. </p> 599 * 600 * @param input the {@code String} to unescape, may be null 601 * @return a new unescaped {@code String}, {@code null} if null string input 602 * @see #escapeXml(String) 603 */ 604 public static final String unescapeXml(final String input) { 605 return UNESCAPE_XML.translate(input); 606 } 607 608 609 //----------------------------------------------------------------------- 610 611 /** 612 * <p>Returns a {@code String} value for a CSV column enclosed in double quotes, 613 * if required.</p> 614 * 615 * <p>If the value contains a comma, newline or double quote, then the 616 * String value is returned enclosed in double quotes.</p> 617 * </p> 618 * 619 * <p>Any double quote characters in the value are escaped with another double quote.</p> 620 * 621 * <p>If the value does not contain a comma, newline or double quote, then the 622 * String value is returned unchanged.</p> 623 * </p> 624 * 625 * see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and 626 * <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>. 627 * 628 * @param input the input CSV column String, may be null 629 * @return the input String, enclosed in double quotes if the value contains a comma, 630 * newline or double quote, {@code null} if null string input 631 * @since 2.4 632 */ 633 public static final String escapeCsv(final String input) { 634 return ESCAPE_CSV.translate(input); 635 } 636 637 /** 638 * <p>Returns a {@code String} value for an unescaped CSV column. </p> 639 * 640 * <p>If the value is enclosed in double quotes, and contains a comma, newline 641 * or double quote, then quotes are removed. 642 * </p> 643 * 644 * <p>Any double quote escaped characters (a pair of double quotes) are unescaped 645 * to just one double quote. </p> 646 * 647 * <p>If the value is not enclosed in double quotes, or is and does not contain a 648 * comma, newline or double quote, then the String value is returned unchanged.</p> 649 * </p> 650 * 651 * see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and 652 * <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>. 653 * 654 * @param input the input CSV column String, may be null 655 * @return the input String, with enclosing double quotes removed and embedded double 656 * quotes unescaped, {@code null} if null string input 657 * @since 2.4 658 */ 659 public static final String unescapeCsv(final String input) { 660 return UNESCAPE_CSV.translate(input); 661 } 662 663}