2

I would like to check that string that is a filename contains a illegal argument from the ILLEGAL_CHARACTERS. Simply I could use for loop, but I want to do this by Streams.

My code:

package shared;

import java.util.Arrays;

public class Validator {
    private static final Character[] ILLEGAL_CHARACTERS =
            {'/','\n','\r','\t','\0','\f','`','?','*','\\','<','>','|','\"',':'};

    public static boolean fileNameIsValid(String fileName) {
        return Arrays.stream(ILLEGAL_CHARACTERS).anyMatch(fileName::contains);
    }
}

Problem is in contains method cuz It needs a CharSequence instead of char. Is there any way to do this by stream without changing Character[] type to String[]?

Oskar
  • 379
  • 4
  • 21
  • 1
    *"I want to do this by Streams"* In my opinion, you should be wanting to do this using regex. – Andreas Mar 12 '21 at 09:20
  • 1
    .. another way to deal with the presence check of a `Character` in a `String` is to get the `indexOf` that character. – Naman Mar 12 '21 at 09:21
  • 1
    Why don't you just turn the characters into strings? I.e. `String[] ILLEGAL_CHARACTERS = { "/", "\n", "\r", ... };` – Andreas Mar 12 '21 at 09:22

4 Answers4

5

Streams might not the best fit here. Also, now your solution has quadratic complexity (N*M, where N is the file name length, and M is the size of your illegal character array), which is not very performant. As proposed in the comments, you could use a regular expression:

private static final Pattern ILLEGAL_CHARACTERS_REGEX =
        Pattern.compile("[/\n\r\t\0\f`?*\\\\<>|\":]");

public static boolean fileNameIsValidRegex(String fileName) {
    return !ILLEGAL_CHARACTERS_REGEX.matcher(fileName).find();
}

Or, if your illegal character set is limited to ASCII, you could play around with a bitset to squeeze some performance:

private static final BitSet ILLEGAL_CHARACTERS = new BitSet();

static {
    for (char c : new char[]{
            '/','\n','\r','\t','\0','\f','`','?','*','\\','<','>','|','\"',':'}) {
        ILLEGAL_CHARACTERS.set(c);
    }
}

public static boolean fileNameIsValid(String fileName) {
    return fileName.chars().noneMatch(ILLEGAL_CHARACTERS::get);
}
Forketyfork
  • 7,416
  • 1
  • 26
  • 33
3

You can try to use indexOf:

return Arrays.stream(ILLEGAL_CHARACTERS)
             .map(fileName::indexOf)
             .anyMatch(i -> i >= 0);
Alex Sveshnikov
  • 4,214
  • 1
  • 10
  • 26
2

First, I would recommend that you use a Set instead of an array, because you don't have any need for indexing your stuff, then Stream over the characters from the given string, and check if there is any match with your set.

Get the chars from the chars() method on a string, this will give you a array of integeres, which you then can cast to an char "array"

Here's all you need:

private static final Set<Character> ILLEGAL_CHARACTERS = Set.of(
        '/','\n','\r','\t','\0','\f','`','?','*','\\','<','>','|','\"',':');

public static boolean fileNameIsValid(String fileName) {
    return fileName.chars()
            .mapToObj(c -> (char) c)
            .noneMatch(ILLEGAL_CHARACTERS::contains);
}
0

If contains method needs a CharSequence instead of char, then you can give it to it:

Arrays.stream(ILLEGAL_CHARACTERS)
        .map(String::valueOf)
        .anyMatch(fileName::contains);

But under the hood in the String class, this method uses indexOf(String str) method:

public boolean contains(CharSequence s) {
    return indexOf(s.toString()) > -1;
}

So, to avoid redundant type conversion, you can use another indexOf(int ch) method:

Arrays.stream(ILLEGAL_CHARACTERS).anyMatch(ch -> fileName.indexOf(ch) > -1);

See also: How to “subtract” 2 given strings In Java?