LANG-374 - Add escaping for CSV columns to StringEscapeUtils
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@595541 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9d138fd0c0
commit
38760d2171
|
@ -688,4 +688,93 @@ public class StringEscapeUtils {
|
||||||
return StringUtils.replace(str, "'", "''");
|
return StringUtils.replace(str, "'", "''");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns a <code>String</code> value for a CSV column escaping with double quotes,
|
||||||
|
* if required.</p>
|
||||||
|
*
|
||||||
|
* <p>If the value contains a comma, newline or double quote, then the
|
||||||
|
* String value is returned enclosed in double quotes.</p>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Any double quote characters in the value are escaped with another double quote.</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 string to escape, may be null
|
||||||
|
* @return a new String, escaped for CSV, <code>null</code> if null string input
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
public static String escapeCsv(String str) {
|
||||||
|
if (!containsCsvChars(str)) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
StringBuffer buffer = new StringBuffer(str.length() + 10);
|
||||||
|
buffer.append('"');
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
char c = str.charAt(i);
|
||||||
|
if (c == '"') {
|
||||||
|
buffer.append('"'); // escape double quote
|
||||||
|
}
|
||||||
|
buffer.append(c);
|
||||||
|
}
|
||||||
|
buffer.append('"');
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Writes a <code>String</code> value for a CSV column escaping with double quotes,
|
||||||
|
* if required.</p>
|
||||||
|
*
|
||||||
|
* <p>If the value contains a comma, newline or double quote, then the
|
||||||
|
* String value is written enclosed in double quotes.</p>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Any double quote characters in the value are escaped with another double quote.</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 string to escape, may be null
|
||||||
|
* @param out Writer to write escaped string into
|
||||||
|
* in double quotes or only when the value contains double quotes, commas or newline
|
||||||
|
* characters.
|
||||||
|
* @throws IOException if error occurs on underlying Writer
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
public static void escapeCsv(Writer out, String str) throws IOException {
|
||||||
|
if (!containsCsvChars(str)) {
|
||||||
|
if (str != null) {
|
||||||
|
out.write(str);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out.write('"');
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
char c = str.charAt(i);
|
||||||
|
if (c == '"') {
|
||||||
|
out.write('"'); // escape double quote
|
||||||
|
}
|
||||||
|
out.write(c);
|
||||||
|
}
|
||||||
|
out.write('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the String contains any characters that need escaping for CSV files.
|
||||||
|
*
|
||||||
|
* @param str the string to escape, may be null
|
||||||
|
* @return <code>true</code> if the String contains characters that need escaping
|
||||||
|
* for CSV files, otherwise <code>false</code>
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
private static boolean containsCsvChars(String str) {
|
||||||
|
return (StringUtils.contains(str, '"') ||
|
||||||
|
StringUtils.contains(str, ',') ||
|
||||||
|
StringUtils.contains(str, CharUtils.CR) ||
|
||||||
|
StringUtils.contains(str, CharUtils.LF));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,4 +332,36 @@ public class StringEscapeUtilsTest extends TestCase {
|
||||||
assertEquals("& &", StringEscapeUtils.unescapeHtml("& &"));
|
assertEquals("& &", StringEscapeUtils.unescapeHtml("& &"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testEscapeCsvString() throws Exception
|
||||||
|
{
|
||||||
|
assertEquals("foo.bar", StringEscapeUtils.escapeCsv("foo.bar"));
|
||||||
|
assertEquals("\"foo,bar\"", StringEscapeUtils.escapeCsv("foo,bar"));
|
||||||
|
assertEquals("\"foo\nbar\"", StringEscapeUtils.escapeCsv("foo\nbar"));
|
||||||
|
assertEquals("\"foo\rbar\"", StringEscapeUtils.escapeCsv("foo\rbar"));
|
||||||
|
assertEquals("\"foo\"\"bar\"", StringEscapeUtils.escapeCsv("foo\"bar"));
|
||||||
|
assertEquals("", StringEscapeUtils.escapeCsv(""));
|
||||||
|
assertEquals(null, StringEscapeUtils.escapeCsv(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEscapeCsvWriter() throws Exception
|
||||||
|
{
|
||||||
|
checkCsvEscapeWriter("foo.bar", "foo.bar");
|
||||||
|
checkCsvEscapeWriter("\"foo,bar\"", "foo,bar");
|
||||||
|
checkCsvEscapeWriter("\"foo\nbar\"", "foo\nbar");
|
||||||
|
checkCsvEscapeWriter("\"foo\rbar\"", "foo\rbar");
|
||||||
|
checkCsvEscapeWriter("\"foo\"\"bar\"", "foo\"bar");
|
||||||
|
checkCsvEscapeWriter("", null);
|
||||||
|
checkCsvEscapeWriter("", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCsvEscapeWriter(String expected, String value) {
|
||||||
|
try {
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
StringEscapeUtils.escapeCsv(writer, value);
|
||||||
|
assertEquals(expected, writer.toString());
|
||||||
|
} catch (IOException e) {
|
||||||
|
fail("Threw: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue