web-dev-qa-db-ja.com

クロスプラットフォームのJavaファイル名の特殊文字を削除する方法はありますか?

オンラインで取得したデータに基づいてファイルの名前を変更するクロスプラットフォームアプリケーションを作成しています。現在のプラットフォームのWeb APIから取得した文字列をサニタイズしたいのですが。

プラットフォームごとにファイル名の要件が異なることがわかっているので、これを行うためのクロスプラットフォームの方法があるかどうか疑問に思っていましたか?

編集: Windowsプラットフォームでは、疑問符 '?'は使用できませんLinuxでは可能ですが、ファイル名で。ファイル名にそのような文字が含まれている可能性があります。これらの文字をサポートするプラットフォームで保持するためにそれらを保持しますが、それ以外の場合は削除します。

また、サードパーティのライブラリを必要としない標準のJavaソリューションをお勧めします。

53
Ben S

他の場所で提案されているように、これは通常、実行したいことではありません。通常、File.createTempFile()などの安全なメソッドを使用して一時ファイルを作成するのが最善です。

ホワイトリストを使用してこれを行うのではなく、「良い」文字のみを保持してください。ファイルが漢字のみで構成されている場合は、すべてを取り除きます。このため、ホワイトリストは使用できません。ブラックリストを使用する必要があります。

Linuxは、本当の苦痛となる可能性のあるものをほとんど許可します。 LinuxをWindowsと同じリストに制限するだけで、将来的に頭痛の種を省くことができます。

WindowsでこのC#スニペットを使用して、Windowsでは無効な文字のリストを作成しました。このリストには、あなたが思っているよりもかなり多くの文字(41)が含まれているため、独自のリストを作成することはお勧めしません。

        foreach (char c in new string(Path.GetInvalidFileNameChars()))
        {
            Console.Write((int)c);
            Console.Write(",");
        }

簡単なJavaクラスはファイル名を「クリーンアップ」します。

public class FileNameCleaner {
final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};
static {
    Arrays.sort(illegalChars);
}
public static String cleanFileName(String badFileName) {
    StringBuilder cleanName = new StringBuilder();
    for (int i = 0; i < badFileName.length(); i++) {
        int c = (int)badFileName.charAt(i);
        if (Arrays.binarySearch(illegalChars, c) < 0) {
            cleanName.append((char)c);
        }
    }
    return cleanName.toString();
}
}

編集:スティーブンが示唆したように、これらのファイルアクセスが許可したディレクトリ内でのみ発生することも確認する必要があります。

次の回答には、Javaでカスタムセキュリティコンテキストを確立し、その「サンドボックス」でコードを実行するためのサンプルコードがあります。

安全なJEXL(スクリプト)サンドボックスをどのように作成しますか?

26
Sarel Botha

または単にこれを行います:

String filename = "A20/B22b#öA\\BC#Ä$%ld_ma.la.xps";
String sane = filename.replaceAll("[^a-zA-Z0-9\\._]+", "_");

結果:A20_B22b_A_BC_ld_ma.la.xps

説明:

[a-zA-Z0-9\\._]は、a〜zの小文字、大文字、数字、ドット、アンダースコアに一致します

[^a-zA-Z0-9\\._]はその逆です。つまり、最初の表現に一致しないすべての文字

[^a-zA-Z0-9\\._]+は、最初の式に一致しない文字のシーケンスです

したがって、a〜z、0〜9、またはからの文字で構成されていないすべての文字シーケンス。 _は置き換えられます。

19
Dirk

これは Sarel Botha による承認済みの回答に基づいています。これは Basic Multilingual Plane の外の文字に遭遇しない限り問題なく機能します。 Unicodeを完全にサポートする必要がある場合(必要がない場合)、代わりに次のコードを使用してください。

public class FileNameCleaner {
  final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};

  static {
    Arrays.sort(illegalChars);
  }

  public static String cleanFileName(String badFileName) {
    StringBuilder cleanName = new StringBuilder();
    int len = badFileName.codePointCount(0, badFileName.length());
    for (int i=0; i<len; i++) {
      int c = badFileName.codePointAt(i);
      if (Arrays.binarySearch(illegalChars, c) < 0) {
        cleanName.appendCodePoint(c);
      }
    }
    return cleanName.toString();
  }
}

ここでの主な変更点:

  • codePointCount i.c.wを使用します。 lengthの代わりにlength
  • charAtの代わりに codePointAt を使用します
  • appendの代わりに appendCodePoint を使用します
  • charsをintsにキャストする必要はありません。実際、charsは基本的にBMPの外部で壊れているため、決して処理しないでください。
13
Stijn de Witt

かなり良い組み込みのJavaソリューション-Character.isXxx()があります。

Character.isJavaIdentifierPart(c)を試してください:

String name = "name.é[email protected]#$%^&*(){}][/=?+-_\\|;:`~!'\",<>";
StringBuilder filename = new StringBuilder();

for (char c : name.toCharArray()) {
  if (c=='.' || Character.isJavaIdentifierPart(c)) {
    filename.append(c);
  }
}

結果は "name.é$ _"です。

6
David Carboni

これが私が使用するコードです:

public static String sanitizeName( String name ) {
    if( null == name ) {
        return "";
    }

    if( SystemUtils.IS_OS_LINUX ) {
        return name.replaceAll( "/+", "" ).trim();
    }

    return name.replaceAll( "[\u0001-\u001f<>:\"/\\\\|?*\u007f]+", "" ).trim();
}

SystemUtilsApache commons-lang からのものです

6
Aaron Digulla

質問からは明らかではありませんが、Webフォーム(?)からパス名を受け入れることを計画しているので、特定の名前を変更する試みをブロックする必要があります。例えば「C:\ Program Files」。これは、「。」を削除するためにパス名を正規化する必要があることを意味します。アクセスチェックを行う前に「..」.

それを考えると、私は違法な文字を削除しようとはしません。代わりに、「new File(str).getCanonicalFile()」を使用して正規パスを作成し、次にサンドボックスの制限を満たしていることを確認して、最後に「File.exists()」、「File.isFile()」を使用しますなど、ソースと宛先がコーシャであり、同じファイルシステムオブジェクトではないことを確認します。操作を試みて例外をキャッチすることで、違法な文字に対処します。

5
Stephen C

[A-Za-z0-9]などを使用する場合は、 MS命名規則 をチェックし、「...から整数表現が1から31 ...」、アーロンディグラの例のように。コード、例えばデビッド・カルボニからはこれらのイワナには十分ではないでしょう.

予約文字のリストを含む抜粋:

Unicode文字や拡張文字セット(128〜255)の文字を含む、現在のコードページの任意の文字を名前に使用します。ただし、次のものは除きます。

次の予約文字:

  • < (未満)
  • >(より大きい)
  • :(コロン)
  • "(二重引用符)
  • /(スラッシュ)
  • \(バックスラッシュ)
  • |(垂直バーまたはパイプ)
  • ?(疑問符)
  • *(アスタリスク)
  • 整数値ゼロ。ASCII NUL文字と呼ばれることもあります。
  • 整数表現が1〜31の範囲の文字。ただし、これらの文字が許可されている代替データストリームは除きます。ファイルストリームの詳細については、「ファイルストリーム」を参照してください。
  • ターゲットファイルシステムで許可されていないその他の文字。
0
wandlang

Paths.get(...)は、不正な文字の位置を含む詳細な例外をスローします。

public static String removeInvalidChars(final String fileName)
{
  try
  {
    Paths.get(fileName);
    return fileName;
  }
  catch (final InvalidPathException e)
  {
    if (e.getInput() != null && e.getInput().length() > 0 && e.getIndex() >= 0)
    {
      final StringBuilder stringBuilder = new StringBuilder(e.getInput());
      stringBuilder.deleteCharAt(e.getIndex());
      return removeInvalidChars(stringBuilder.toString());
    }
    throw e;
  }
}
0
l.poellabauer