Today I needed some code in a Java application that would create a file with a valid file name, based on some user input. Of course, Java is a very portable language, so it was no good to only remove characters invalid in Windows, or Linux. I wanted something as portable as possible and therefore likely to work on many platform. Fortunately, most modern OS’s are POSIX compliant.
So I created this little method to convert strings to POSIX-compliant, and thus portable, file names.
I took the rules from Wikipedia’s page on Filename, which cited “Lewine, Donald. POSIX Programmer’s Guide: Writing Portable UNIX Programs 1991 O’Reilly & Associates, Inc. Sebastopol, CA pp63-64”, which are:
- Maximum of 14 characters long.
- Must not have a hyphen as the first character.
- Can only contain the following letters: A-Z, a-z, 0-9, . (period), _ (underscore), and – (hyphen/minus/dash) .
/** * Creates a POSIX-compliant file name based on the passed string. The returned string will be: * <ol> * <li>14 characters or fewer.</li> * <li>Made up only of the following characters: A–Z a–z 0–9 . (period) _ (underscore) - (minus/hyphen).</li> * <li>The first character may not be a minus/hyphen.</li> * </ol> * @param str a string that needs to be converted to a file name. * @return a string that only contains POSIX-compliant filename characters and length */ public static String convertToPOSIXCompliantFilename(String str) { if (str == null) return null; StringBuilder sb = new StringBuilder(); for (int i = 0; i < str.length() && sb.length() < 14; i++) { char ch = str.charAt(i); if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '.') || (ch == '_') || ((ch == '-') && sb.length()>0)) { sb.append(ch); } } return sb.toString(); }
And some unit tests:
@Test public void testPOSIXFileNameConverter() throws Exception { String[] validTestCases = new String[] { "ABCDEFGHIJKLM", "NOPQRSTUVWXYZ", "abcdefghijklm", "nopqrstuvwxyz", "0123456789_.-", "Normal.file" }; for (String testCase : validTestCases) { assertEquals(testCase, FileUtility.convertToPOSIXCompliantFilename(testCase)); } String[] adjustedTestCases = new String[] { "-----", "", "ABC!"£$%^-", "ABC-", " A", "A", "1234567890ABCDEFGH", "1234567890ABCD", "----1234567890ABCDEFGH", "1234567890ABCD"}; for (int i = 0; i < adjustedTestCases.length - 1; i += 2) { assertEquals(adjustedTestCases[i + 1], FileUtility.convertToPOSIXCompliantFilename(adjustedTestCases[i])); } }
Nothing particularly clever, but I suspect I’ll be using this method whenever the user has some influence on files created by my code. This code is public domain and Locima provides no guarantees of it’s correctness, or any warranties. Naturally.