How do you escape colon (:) in Properties file? - java

I am using a properties file to store my application's configuration values.
In one of the instances, I have to store a value as
xxx:yyy:zzz. When I do that, the colon is escaped with a back slash\ resulting in the value showing as xxx\:yyy\:zzz in the properties file.
I am aware that the colon : is a standard delimiter of the Properties Java class. However I still need to save the value without the back slash \.
Any suggestions on how to handle this?

Put the properties into the Properties object and save it using a store(...) method. The method will perform any escaping required. The Java documentation says:
"... For the key, all space characters are written with a preceding \ character. For the element, leading space characters, but not embedded or trailing space characters, are written with a preceding \ character. The key and element characters #, !, =, and : are written with a preceding backslash to ensure that they are properly loaded."
You only need to manually escape characters if you are creating / writing the file by hand.
Conversely, if you want the file to contain unescaped colon characters, you are out of luck. Such a file is malformed and probably won't load properly using the Properties.load(...) methods. If you go down this route, you'll need to implement your own custom load and/or store methods.

I came across the same issue. Forward slashes / also get escaped by the store() method in Properties.
I solved this issue by creating my own CustomProperties class (extending java.util.Properties) and commenting out the call to saveConvert() in the customStore0() method.
Here is my CustomProperties class:
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
public class CustomProperties extends Properties {
private static final long serialVersionUID = 1L;
#Override
public void store(OutputStream out, String comments) throws IOException {
customStore0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
comments, true);
}
//Override to stop '/' or ':' chars from being replaced by not called
//saveConvert(key, true, escUnicode)
private void customStore0(BufferedWriter bw, String comments, boolean escUnicode)
throws IOException {
bw.write("#" + new Date().toString());
bw.newLine();
synchronized (this) {
for (Enumeration e = keys(); e.hasMoreElements();) {
String key = (String) e.nextElement();
String val = (String) get(key);
// Commented out to stop '/' or ':' chars being replaced
//key = saveConvert(key, true, escUnicode);
//val = saveConvert(val, false, escUnicode);
bw.write(key + "=" + val);
bw.newLine();
}
}
bw.flush();
}
}

We hit this question a couple of days ago. We were manipulating existing properties files with URLs as values.
It's risky but if your property values are less than 40 characters then you can use the "list" method instead of "store":
http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#list(java.io.PrintWriter)
We had a quick look at the JDK code and hacked out a custom implementation of store that works for our purposes:
public void store(Properties props, String propertyFilePath) throws FileNotFoundException {
PrintWriter pw = new PrintWriter(propertyFilePath);
for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
String key = (String) e.nextElement();
pw.println(key + "=" + props.getProperty(key));
}
pw.close();
}

If you use the xml variant of the properties file (using loadFromXML and storeToXML) this shouldn't be a problem.

Try using unicode.
The unicode for a colon is\u003A
Additionally the unicode for a space is: \u0020
For a list of basic Latin characters see: https://en.wikipedia.org/wiki/Basic_Latin_(Unicode_block)
For example:
ProperName\u003A\NameContinues=Some property value
Will expect a property with a key:
ProperName:NameContinues
And will have a value of:
Some property value

For me it worked by using \ before special character,
e.g,
Before: VCS\u003aIC\u0020Server\u003a=Migration
After: VCS\:IC\ Server\:=Migration
: is escaped with \: and (space) with \ (\ followed by <Space>).
For more info : https://en.wikipedia.org/wiki/.properties

For people like me that get here for this when using Spring Boot configuration properties files: You need to enclose in [..]:
E.g.:
my.test\:key=value
is not enough, you need this in your application.properties for example:
my.[test\:key]=value
See also SpringBoot2 ConfigurationProperties removes colon from yaml keys

Its simple,
just use Apostrophe ' ' over there
E.g.:
Instead of this(case 1)
File file= new File("f:\\properties\\gog\\esave\\apple");
prop.setProperty("basedir",file.toString());
Use this(case 2)
File file= new File("f':'\\properties\\gog\\esave\\apple");
prop.setProperty("basedir",file.toString());
Output will be
Case 1: basedir = f\:\\properties\\gog\\esave\\apple
Case 2: basedir = f:\\properties\\gog\\esave\\apple
I hope this will help you

Related

How to avoid backslash before comma in CSVFormat

I am creating a CSV file using CSVFormat in java, the problem i am facing in both header and values is whenever the string is long and there is a comma the api is inserting a \ before the comma always. As a result the header is not forming correctly and the values in the csv file is taking next cell for the . I am posting the code what i have done
try (CSVPrinter csvPrinter = new CSVPrinter(out,
CSVFormat.DEFAULT.withHeader("\""+SampleEnum.MY_NAME.getHeader()+"\"", "\""+SampleEnum.MY_TITLE.getHeader()+"\"",
"\""+SampleEnum.MY_ID.getHeader()+"\"", "\""+SampleEnum.MY_NUMBER.getHeader()+"\"", "\""+SampleEnum.MY_EXTERNAL_KEY.getHeader()+"\"",
"\""+SampleEnum.DATE.getHeader()+"\"","\""+SampleEnum.MY_ACTION.getHeader()+"\"",
"\"\"\""+SampleEnum.MY__DEFI.getHeader()+"\"\"\"", SampleEnum.MY_ACTION.getHeader(),
SampleEnum.CCHK.getHeader(), SampleEnum.DISTANCE_FROM_LOCATION.getHeader(),
SampleEnum.TCOE.getHeader(), SampleEnum.HGTR.getHeader(),SampleEnum._BLANK.getHeader(),
SampleEnum.LOCATION_MAP.getHeader(), SampleEnum.SUBMISSION_ID.getHeader())
.withDelimiter(',').withEscape('\\').withQuote('"').withTrim().withQuoteMode(QuoteMode.MINIMAL)
)) {
sampleModel.forEach(sf -> {
try {
csvPrinter.printRecord(sf.getMyName(),
sf.getMyTitle(),
sf.getMyID(),
sf.getMyNo(),
So now the problem is i am getting output like this
"\"Name:\"","\"Title\"","\"ID #:\"","\"Store #:\"","\"Store #: External Key\"","\"Date:\"","\"\"\"It's performance issue in detail to include dates,times, circumstances, etc.\"\"\""
I am getting \ before each commas , and when this will come in the value the next portion of the text will shift to the next cell .
my Required output is
"Name:","Title:","Employee ID #:","Store #:","Store #: CurrierKey","Date:","Stage of Disciplinary Action:","""Describe your view about the company, times, circumstances, etc.""",
I am trying
https://commons.apache.org/proper/commons-csv/jacoco/org.apache.commons.csv/CSVFormat.java.html
this link, but i am unable to understand the fix. Please help .
This happens because you are using QuoteMode.NONE which has the following Javadoc:
Never quotes fields. When the delimiter occurs in data, the printer prefixes it with the escape character. If the escape character is not set, format validation throws an exception.
You can use QuoteMode.MINIMAL to only quotes fields which contain special characters (e.g. the field delimiter, quote character or a character of the line separator string).
I suggest that you use CSVFormat.DEFAULT and then configure everything yourself if you cannot use one of the other formats. Check if the backslash (\) is really the right escape character for your use case. Normally it would be a double quote ("). Also, you probably want to remove all the double quotes from your header definition as they get added automatically (if necessary) based on your configuration.
StringBuilder out = new StringBuilder();
try (CSVPrinter csvPrinter = new CSVPrinter(out,
CSVFormat.DEFAULT
.withHeader("AAAA", "BB\"BB", "CC,CC", "DD'DD")
.withDelimiter(',')
.withEscape('\\') // <- maybe you want '"' instead
.withQuote('"').withRecordSeparator('\n').withTrim()
.withQuoteMode(QuoteMode.MINIMAL)
)) {
csvPrinter.printRecord("WWWW", "XX\"XX", "YY,YY", "ZZ'ZZ");
}
System.out.println(out);
AAAA,"BB\"BB","CC,CC",DD'DD
WWWW,"XX\"XX","YY,YY",ZZ'ZZ
After your edit, it seems like you want all fields to be quoted with a double quote as escape character. Therefore, you can use QuoteMode.ALL and .withEscape('"') like this:
StringBuilder out = new StringBuilder();
try (CSVPrinter csvPrinter = new CSVPrinter(out,
CSVFormat.DEFAULT
.withHeader("AAAA", "BB\"BB", "CC,CC", "\"DD\"", "1")
.withDelimiter(',')
.withEscape('"')
.withQuote('"').withRecordSeparator('\n').withTrim()
.withQuoteMode(QuoteMode.ALL)
)) {
csvPrinter.printRecord("WWWW", "XX\"XX", "YY,YY", "\"DD\"", "2");
}
System.out.println(out);
"AAAA","BB""BB","CC,CC","""DD""","1"
"WWWW","XX""XX","YY,YY","""DD""","2"
In your comment, you state that you only want double quotes when required and triple quotes for one field only. Then, you can use QuoteMode.MINIMAL and .withEscape('"') as suggested in the first example. The triple quotes get generated when you surround your input of that field with double quotes (once because there is a special character and the field needs to be quoted, the second one because you added your explicit " and the third one is there to escape your explicit quote).
StringBuilder out = new StringBuilder();
try (CSVPrinter csvPrinter = new CSVPrinter(out,
CSVFormat.DEFAULT
.withHeader("AAAA", "BB\"BB", "CC,CC", "\"DD\"", "1")
.withDelimiter(',')
.withEscape('"')
.withQuote('"').withRecordSeparator('\n').withTrim()
.withQuoteMode(QuoteMode.MINIMAL)
)) {
csvPrinter.printRecord("WWWW", "XX\"XX", "YY,YY", "\"DD\"", "2");
}
System.out.println(out);
AAAA,"BB""BB","CC,CC","""DD""",1
WWWW,"XX""XX","YY,YY","""DD""",2
As per the chat you want total control when the header has quotes and when not. There is no combination of QuoteMode and escape character that will give the desired result. Consequently, I suggest that you manually construct the header:
StringBuilder out = new StringBuilder();
try (CSVPrinter csvPrinter = new CSVPrinter(out,
CSVFormat.DEFAULT
.withDelimiter(',').withEscape('"')
.withQuote('"').withRecordSeparator('\n').withTrim()
.withQuoteMode(QuoteMode.MINIMAL))
) {
out.append(String.join(",", "\"AAAA\"", "\"BBBB\"", "\"CC,CC\"", "\"\"\"DD\"\"\"", "1"));
out.append("\n");
csvPrinter.printRecord("WWWW", "XX\"XX", "YY,YY", "\"DD\"", "2");
}
System.out.println(out);
"AAAA","BBBB","CC,CC","""DD""",1
WWWW,"XX""XX","YY,YY","""DD""",2

How to dynamically update absolute path

Given the below incoming path, e.g.
C:\cresttest\parent_3\child_3_1\child_3_1_.txt
How can one update and add new dir in between above path to construct below result
C:\cresttest\NEW_PATH\parent_3\child_3_1\child_3_1_.txt
Currently I am using multiple subString to identify the incoming path, but incoming path are random and dynamic. Using substring and placing my new path requires more line of code or unnecessary processing, is there any API or way to easily update and add my new dir in between the absolute path?
By using java.nio.file.Path, you could to the following:
Path incomingPath = Paths.get("C:\\cresttest\\parent_3\\child_3_1\\child_3_1_.txt");
//getting C:\cresttest\, adding NEW_PATH to it
Path subPathWithAddition = incomingPath.subpath(0, 2).resolve("NEW_PATH");
//Concatenating C:\cresttest\NEW_PATH\ with \parent_3\child_3_1\child_3_1_.txt
Path finalPath = subPathWithAddition.resolve(incomingPath.subpath(2, incomingPath.getNameCount()));
You could then get the path URI by calling finalPath.toUri()
Note: this doesn't depend on any names in your path, it depends on the directory depth though, which you could edit in the subpath calls.
Note 2: you could probably reduce the amount of Path instances you make to one, I made three to improve readability.
You may simply insert a path at the second backslash like this:
String path="C:\\cresttest\\parent_3\\child_3_1\\child_3_1_.txt";
final String slash="\\\\";
path=path.replaceFirst(slash+"[^"+slash+"]+"+slash, "$0NEW_PATH"+slash);
System.out.println(path);
Demo
This replaces the first occurrence of \\arbitrarydirname\\ with itself (referred to via $0) followed by NEWPATH\\.
The separator’s source code representation looks a bit odd ("\\\\") as a backslash has to be escaped twice when writing regular expression in a Java String literal.
If you want your operation to be platform independent, you may replace that line with
final String slash = Pattern.quote(FileSystems.getDefault().getSeparator());
Of course, then, the input path must be in the right format for the platform as well.
You can use this simple regex replace:
path = path.replaceAll(":.\\w+", "$0\\\\NEW_PATH");
Your code would be simpler if you used / instead of \ for your path delimiters. eg, compare:
String path = "C:\\cresttest\\parent_3\\child_3_1\\child_3_1_.txt";
path = path.replaceAll(":.\\w+", "$0\\\\NEW_PATH");
with
String path = "C:/cresttest/parent_3.child_3_1/child_3_1_.txt";
path = path.replaceAll(":.\\w+", "$0/NEW_PATH");
Java can handle either delimiter on windows, but on linux only / works, so to make your code portable and more readable, prefer using /.
Just for fun, not sure whether this is what you wanted
public static String addFolderToPath(String originalPath, String newFolderName, int position){
String returnString = "";
String[] pathArray = originalPath.split("\\\\");
for(int i = 0; i<pathArray.length; i++){
returnString = returnString.concat(i==position ? "\\" + newFolderName : "");
returnString = returnString.concat(i!=0 ? "\\" + pathArray[i] : "" + pathArray[i]);
}
return returnString;
}
Call:
System.out.println(addFolderToPath("c:\\abc\\def\\ghi\\jkl", "test", 1));
System.out.println(addFolderToPath("c:\\abc\\def\\ghi\\jkl", "test", 2));
System.out.println(addFolderToPath("c:\\abc\\def\\ghi\\jkl", "test", 3));
System.out.println(addFolderToPath("c:\\abc\\def\\ghi\\jkl", "test", 4));
Run:
c:\test\abc\def\ghi\jkl
c:\abc\test\def\ghi\jkl
c:\abc\def\test\ghi\jkl
c:\abc\def\ghi\test\jkl

Regex to fetch only Key Value pairs and omit some charaters

I have this data
ReferenceDataLocation = as
##############################################################################
#
# LicenseKey
# Address Doctor License
#
##############################################################################
LicenseKey = al
which I'd like to capture only key value pairs eg: ReferenceDataLocation = as and LicenseKey = al
I wrote (?xms)(^[\w]+.*?)(?=^[\w]+|\z) regex which is perfect except the fact that it also captures ##### part, which is not key value pair.
Please help me modify the same regex (?xms)(^[\w]+.*?)(?=^[\w]+|\z) to only get ReferenceDataLocation = as and LicenseKey = al
Note: Here you can try out
Update
I tried (?xms)(^[\w]+.*?)(?=^[\w^#]+|\z) it works in the site but gives me an error in java
Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed character class near index 31
(?xms)(^[\w]+.*?)(?=^[\w^#]+|\Z)
^
Updat Regex that works for me
(?xms)(^[\w]+.*?)(?=^[\w^\s]+|\z)
You can't do that with a simple regex-match. You can't account for occurrences like these:
# some words here LicenseKey = al
The regex engine cannot look behind from LicenseKey to the end of the line. This is not supported in Java's regex engine (unbounded look-behinds).
But what you posted looks like it's just a properties file. Try this:
import java.io.FileInputStream;
import java.util.Properties;
public class Main {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.load(new FileInputStream("test.properties"));
System.out.println(properties.getProperty("ReferenceDataLocation"));
System.out.println(properties.getProperty("LicenseKey"));
System.out.println(properties.getProperty("foo"));
}
}
which will print:
as
al
null
Note that your input file needn't be called test.properties, you can give it any name you like.
And if you don't know the keys up front, you can simply iterate over all entries in your properties file like this:
for(Map.Entry<Object, Object> entry : properties.entrySet()) {
System.out.println(entry.getKey() + " :: " + entry.getValue());
}
which prints:
LicenseKey :: al
ReferenceDataLocation :: as
And there's also Properties#stringPropertyNames() which returns a Set<String> that represents all keys in the properties file (see the API docs for more info).
See:
Tutorial: http://download.oracle.com/javase/tutorial/essential/environment/properties.html
API docs: http://download.oracle.com/javase/7/docs/api/java/util/Properties.html

Java use regex to extract file name

I need to get a file name from file's absolute path (I am aware of the file.getName() method, but I cannot use it here).
EDIT: I cannot use file.getName() because I don't need the file name only; I need the part of the file's path as well (but again, not the entire absoulte path). I need the part of file's path AFTER certain path provided.
Let's say the file is located in the folder:
C:\Users\someUser
On windows machine, if I make a pattern string as follows:
String patternStr = "C:\\Users\\someUser\\(.*+)";
I get an exception: java.util.regex.PatternSyntaxException: Illegal/unsupported escape sequence for backslash.
If I use Pattern.quote(File.pathSeparator):
String patternStr = "C:" + Pattern.quote(File.separator) + "Users" + Pattern.quote(File.separator) + "someUser" + Pattern.quote(File.separator) + "(.*+)";
the resulting pattern string is: C:\Q;\EUsers\Q;\EsomeUser\Q;\E(.*+) which of course has no match with the actual fileName "C:\Users\someUser\myFile.txt".
What am I missing here? What is the proper way to parse file name?
What is the proper way to parse file name?
The proper way to parse a file name is to use File(String). Using a regex for this is going to hard-wire platform dependencies into your code. That's a bad idea.
I know you said you can't use File.getName() ... but that is the proper solution. If you would care to say why you can't use File.getName() perhaps I could suggest an alternative solution.
If you indeed want to use a regular expressions, you should use
String patternStr = "C:\\\\Users\\\\someUser\\\\(.*+)";
^^ ^^ ^^
instead.
Why? Your string literal
"C:\\Users\\someUser\\(.*+)"
is compiled to
C:\Users\someUser\(.*+)
Since \ is used for escaping in regular expressions too, you'll have to escape them "twice".
Regarding your edit:
You probably want to have a look at URI.relativize(). Example:
File base = new File("C:/Users/someUser");
File file = new File("C:/Users/someUser/someDir/someFile.txt");
String relativePath = base.toURI().relativize(file.toURI()).getPath();
System.out.println(relativePath); // prints "someDir/someFile.txt"
(Note that / works as file-separator on Windows machines too.)
Btw, I don't know what you have as File.separator on your system, but if it's set to \, then
"C:" + Pattern.quote(File.separator) + "Users" + Pattern.quote(File.separator) +
"someUser" + Pattern.quote(File.separator) + "(.*+)";
should yield
C:\Q\\EUsers\Q\\EsomeUser\Q\\E(.*+)
String patternStr = "C:\\Users\\someUser\\(.*+)";
Backslashes (\) are escape characters in the Java Language. Your string contains the following after compilation:
C:\Users\someUser\(.*+)
This string is then parsed as a regex, which uses backslashes as an escape character as well. The regex parser tries to understand the escaped \U, \s and \(. One of them is incorrect regarding the regex syntax (hence your exception), and none of them are what you are trying to achieve.
Try
String patternStr = "C:\\\\Users\\\\someUser\\\\(.*+)";
If you want to solve it by pattern you need to escape your Pattern properly
String patternStr = "C:\\\\Users\\\\someUser\\\\(.*+)";
Try putting double-double-backslashes in your pattern. You need a second backslash to escape one in the patter, plus you'll need to double each one to escape them in the string. Hence you'll end up with something like:
String patternStr = "C:\\\\Users\\\\someUser\\\\(.*+)";
Move from end of string to first occurrence of file path separator* or begin.
File paths separator can be / or \.
public static final char ALTERNATIVE_DIRECTORY_SEPARATOR_CHAR = '/';
public static final char DIRECTORY_SEPARATOR_CHAR = '\\';
public static final char VOLUME_SEPARATOR_CHAR = ':';
public static String getFileName(String path) {
if(path == null || path.isEmpty()) {
return path;
}
int length = path.length();
int index = length;
while(--index >= 0) {
char c = path.charAt(index);
if(c == ALTERNATIVE_DIRECTORY_SEPARATOR_CHAR || c == DIRECTORY_SEPARATOR_CHAR || c == VOLUME_SEPARATOR_CHAR) {
return path.substring(index + 1, length);
}
}
return path;
}
Try to keep it simple ;-).
Try this :
String ResultString = null;
try {
Pattern regex = Pattern.compile("([^\\\\/:*?\"<>|\r\n]+$)");
Matcher regexMatcher = regex.matcher(subjectString);
if (regexMatcher.find()) {
ResultString = regexMatcher.group(1);
}
} catch (PatternSyntaxException ex) {
// Syntax error in the regular expression
}
Output :
myFile.txt
Also for input : C:/Users/someUser/myFile.txt
Output : myFile.txt
What am I missing here? What is the proper way to parse file name?
The proper way to parse a file name is to use the APIs that are already provided for the purpose. You've stated that you can't use File.getName(), without explanation. You are almost certainly mistaken about that.
I cannot use file.getName() because I don't need the file name only; I need the part of the file's path as well (but again, not the entire absoulte path).
OK. So what you want is something like this.
// Canonicalize paths to deal with ".", "..", symlinks,
// relative files and case sensitivity issues.
String directory = new File(someDirectory).canonicalPath();
String test = new File(somePathname).canonicalPath();
if (!directory.endsWith(File.separator)) {
directory += File.separator;
}
if (test.startsWith(directory)) {
String pathInDirectory = test.substring(directory.length()):
...
}
Advantages:
No regexes needed.
Doesn't break if the path separator is something other than \.
Doesn't break if there are symbolic links on the path.
Doesn't break due to case sensitivity issues.
Suppose the file name has special characters, specially when supporting MAC where special characters are allowing in filenames, server side Path.GetFileName(fileName) fails and throws error because of illegal characters in path. The following code using regex come for the rescue.
The following regex take care of 2 things
In IE, when file is uploaded, the file path contains folders aswell (i.e. c:\samplefolder\subfolder\sample.xls). Expression below will replace all folders with empty string and retain the file name
When used in Mac, filename is the only thing supplied as its safari browser and allows special chars in file name
var regExpDir = #"(^[\w]:\\)([\w].+\w\\)";
var fileName = Regex.Replace(fileName, regExpDir, string.Empty);

Does groovy have an easy way to get a filename without the extension?

Say I have something like this:
new File("test").eachFile() { file->
println file.getName()
}
This prints the full filename of every file in the test directory. Is there a Groovy way to get the filename without any extension? (Or am I back in regex land?)
I believe the grooviest way would be:
file.name.lastIndexOf('.').with {it != -1 ? file.name[0..<it] : file.name}
or with a simple regexp:
file.name.replaceFirst(~/\.[^\.]+$/, '')
also there's an apache commons-io java lib for that kinda purposes, which you could easily depend on if you use maven:
org.apache.commons.io.FilenameUtils.getBaseName(file.name)
The cleanest way.
String fileWithoutExt = file.name.take(file.name.lastIndexOf('.'))
Simplest way is:
'file.name.with.dots.tgz' - ~/\.\w+$/​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
Result is:
file.name.with.dots
new File("test").eachFile() { file->
println file.getName().split("\\.")[0]
}
This works well for file names like:
foo, foo.bar
But if you have a file foo.bar.jar, then the above code prints out: foo
If you want it to print out foo.bar instead, then the following code achieves that.
new File("test").eachFile() { file->
def names = (file.name.split("\\.")
def name = names.size() > 1 ? (names - names[-1]).join('.') : names[0]
println name
}
The FilenameUtils class, which is part of the apache commons io package, has a robust solution. Example usage:
import org.apache.commons.io.FilenameUtils
String filename = '/tmp/hello-world.txt'
def fileWithoutExt = FilenameUtils.removeExtension(filename)
This isn't the groovy way, but might be helpful if you need to support lots of edge cases.
Maybe not as easy as you expected but working:
new File("test").eachFile {
println it.name.lastIndexOf('.') >= 0 ?
it.name[0 .. it.name.lastIndexOf('.')-1] :
it.name
}
As mentioned in comments, where a filename ends & an extension begins depends on the situation. In my situation, I needed to get the basename (file without path, and without extension) of the following types of files: { foo.zip, bar/foo.tgz, foo.tar.gz } => all need to produce "foo" as the filename sans extension. (Most solutions, given foo.tar.gz would produce foo.tar.)
Here's one (obvious) solution that will give you everything up to the first "."; optionally, you can get the entire extension either in pieces or (in this case) as a single remainder (splitting the filename into 2 parts). (Note: although unrelated to the task at hand, I'm also removing the path as well, by calling file.name.)
file=new File("temp/foo.tar.gz")
file.name.split("\\.", 2)[0] // => return "foo" at [0], and "tar.gz" at [1]
You can use regular expressions better.
A function like the following would do the trick:
def getExtensionFromFilename(filename) {
def returned_value = ""
m = (filename =~ /(\.[^\.]*)$/)
if (m.size()>0) returned_value = ((m[0][0].size()>0) ? m[0][0].substring(1).trim().toLowerCase() : "");
return returned_value
}
Note
import java.io.File;
def fileNames = [ "/a/b.c/first.txt",
"/b/c/second",
"c:\\a\\b.c\\third...",
"c:\\a\b\\c\\.text"
]
def fileSeparator = "";
fileNames.each {
// You can keep the below code outside of this loop. Since my example
// contains both windows and unix file structure, I am doing this inside the loop.
fileSeparator= "\\" + File.separator;
if (!it.contains(File.separator)) {
fileSeparator = "\\/"
}
println "File extension is : ${it.find(/((?<=\.)[^\.${fileSeparator}]+)$/)}"
it = it.replaceAll(/(\.([^\.${fileSeparator}]+)?)$/,"")
println "Filename is ${it}"
}
Some of the below solutions (except the one using apache library) doesn't work for this example - c:/test.me/firstfile
If I try to find an extension for above entry, I will get ".me/firstfile" - :(
Better approach will be to find the last occurrence of File.separator if present and then look for filename or extension.
Note:
(There is a little trick happens below. For Windows, the file separator is \. But this is a special character in regular expression and so when we use a variable containing the File.separator in the regular expression, I have to escape it. That is why I do this:
def fileSeparator= "\\" + File.separator;
Hope it makes sense :)
Try this out:
import java.io.File;
String strFilename = "C:\\first.1\\second.txt";
// Few other flavors
// strFilename = "/dd/dddd/2.dd/dio/dkljlds.dd"
def fileSeparator= "\\" + File.separator;
if (!strFilename.contains(File.separator)) {
fileSeparator = "\\/"
}
def fileExtension = "";
(strFilename =~ /((?<=\.)[^\.${fileSeparator}]+)$/).each { match, extension -> fileExtension = extension }
println "Extension is:$fileExtension"
// Create an instance of a file (note the path is several levels deep)
File file = new File('/tmp/whatever/certificate.crt')
// To get the single fileName without the path (but with EXTENSION! so not answering the question of the author. Sorry for that...)
String fileName = file.parentFile.toURI().relativize(file.toURI()).getPath()

Categories