Applying my patch from LANG-374 that adds an unescape method. It stays true to the spec and considers "foo.bar" to unescape as "foo.bar", ie) no unescaping, because "foo.bar" is illegal output from the escape method spec-wise.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@609094 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
87106839de
commit
ea4ce16289
|
@ -772,4 +772,88 @@ public class StringEscapeUtils {
|
|||
out.write(CSV_QUOTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns a <code>String</code> value for an unescaped CSV column. </p>
|
||||
*
|
||||
* <p>If the value is enclosed in double quotes, and contains a comma, newline
|
||||
* or double quote, then quotes are removed.
|
||||
* </p>
|
||||
*
|
||||
* <p>Any double quote escaped characters (a pair of double quotes) are unescaped
|
||||
* to just one double quote. </p>
|
||||
*
|
||||
* <p>If the value is not enclosed in double quotes, or is and does not contain a
|
||||
* comma, newline or double quote, then the String value is returned unchanged.</p>
|
||||
* </p>
|
||||
*
|
||||
* see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
|
||||
* <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
|
||||
*
|
||||
* @param str the input CSV column String, may be null
|
||||
* @return the input String, with enclosing double quotes removed and embedded double
|
||||
* quotes unescaped, <code>null</code> if null string input
|
||||
* @since 2.4
|
||||
*/
|
||||
public static String unescapeCsv(String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
StringWriter writer = new StringWriter();
|
||||
unescapeCsv(writer, str);
|
||||
return writer.toString();
|
||||
} catch (IOException ioe) {
|
||||
// this should never ever happen while writing to a StringWriter
|
||||
ioe.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns a <code>String</code> value for an unescaped CSV column. </p>
|
||||
*
|
||||
* <p>If the value is enclosed in double quotes, and contains a comma, newline
|
||||
* or double quote, then quotes are removed.
|
||||
* </p>
|
||||
*
|
||||
* <p>Any double quote escaped characters (a pair of double quotes) are unescaped
|
||||
* to just one double quote. </p>
|
||||
*
|
||||
* <p>If the value is not enclosed in double quotes, or is and does not contain a
|
||||
* comma, newline or double quote, then the String value is returned unchanged.</p>
|
||||
* </p>
|
||||
*
|
||||
* see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
|
||||
* <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
|
||||
*
|
||||
* @param str the input CSV column String, may be null
|
||||
* @param out Writer to write the input String to, with enclosing double quotes
|
||||
* removed and embedded double quotes unescaped, <code>null</code> if null string input
|
||||
* @throws IOException if error occurs on underlying Writer
|
||||
* @since 2.4
|
||||
*/
|
||||
public static void unescapeCsv(Writer out, String str) throws IOException {
|
||||
if (str == null) {
|
||||
return;
|
||||
}
|
||||
if (str.length() < 2) {
|
||||
out.write(str);
|
||||
return;
|
||||
}
|
||||
if ( str.charAt(0) != CSV_QUOTE || str.charAt(str.length() - 1) != CSV_QUOTE ) {
|
||||
out.write(str);
|
||||
return;
|
||||
}
|
||||
|
||||
// strip quotes
|
||||
String quoteless = str.substring(1, str.length() - 1);
|
||||
|
||||
if ( StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS) ) {
|
||||
// deal with escaped quotes; ie) ""
|
||||
str = StringUtils.replace(quoteless, "" + CSV_QUOTE + CSV_QUOTE, Character.toString(CSV_QUOTE));
|
||||
}
|
||||
|
||||
out.write(str);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -332,7 +332,6 @@ public class StringEscapeUtilsTest extends TestCase {
|
|||
assertEquals("& &", StringEscapeUtils.unescapeHtml("& &"));
|
||||
}
|
||||
|
||||
|
||||
public void testEscapeCsvString() throws Exception
|
||||
{
|
||||
assertEquals("foo.bar", StringEscapeUtils.escapeCsv("foo.bar"));
|
||||
|
@ -364,4 +363,40 @@ public class StringEscapeUtilsTest extends TestCase {
|
|||
fail("Threw: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
public void testUnescapeCsvString() throws Exception
|
||||
{
|
||||
assertEquals("foo.bar", StringEscapeUtils.unescapeCsv("foo.bar"));
|
||||
assertEquals("foo,bar", StringEscapeUtils.unescapeCsv("\"foo,bar\""));
|
||||
assertEquals("foo\nbar", StringEscapeUtils.unescapeCsv("\"foo\nbar\""));
|
||||
assertEquals("foo\rbar", StringEscapeUtils.unescapeCsv("\"foo\rbar\""));
|
||||
assertEquals("foo\"bar", StringEscapeUtils.unescapeCsv("\"foo\"\"bar\""));
|
||||
assertEquals("", StringEscapeUtils.unescapeCsv(""));
|
||||
assertEquals(null, StringEscapeUtils.unescapeCsv(null));
|
||||
|
||||
assertEquals("\"foo.bar\"", StringEscapeUtils.unescapeCsv("\"foo.bar\""));
|
||||
}
|
||||
|
||||
public void testUnescapeCsvWriter() throws Exception
|
||||
{
|
||||
checkCsvUnescapeWriter("foo.bar", "foo.bar");
|
||||
checkCsvUnescapeWriter("foo,bar", "\"foo,bar\"");
|
||||
checkCsvUnescapeWriter("foo\nbar", "\"foo\nbar\"");
|
||||
checkCsvUnescapeWriter("foo\rbar", "\"foo\rbar\"");
|
||||
checkCsvUnescapeWriter("foo\"bar", "\"foo\"\"bar\"");
|
||||
checkCsvUnescapeWriter("", null);
|
||||
checkCsvUnescapeWriter("", "");
|
||||
|
||||
checkCsvUnescapeWriter("\"foo.bar\"", "\"foo.bar\"");
|
||||
}
|
||||
|
||||
private void checkCsvUnescapeWriter(String expected, String value) {
|
||||
try {
|
||||
StringWriter writer = new StringWriter();
|
||||
StringEscapeUtils.unescapeCsv(writer, value);
|
||||
assertEquals(expected, writer.toString());
|
||||
} catch (IOException e) {
|
||||
fail("Threw: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue