2009-07-20 32 views
50

Tôi đang tạo một ứng dụng đa nền tảng để đổi tên các tệp dựa trên dữ liệu được truy xuất trực tuyến. Tôi muốn khử trùng các chuỗi tôi lấy từ một API web cho nền tảng hiện tại.Có phương pháp Java đa nền tảng để xóa các ký tự đặc biệt của tên tệp không?

Tôi biết rằng các nền tảng khác nhau có yêu cầu tên tệp khác nhau, vì vậy tôi đã tự hỏi liệu có một cách đa nền tảng để thực hiện việc này không?

Chỉnh sửa: Trên nền tảng Windows, bạn không thể có dấu chấm hỏi '?' trong một tên tập tin, trong khi trong Linux, bạn có thể. Tên tệp có thể chứa các ký tự như vậy và tôi muốn cho các nền tảng hỗ trợ các ký tự đó để giữ chúng, nhưng nếu không, hãy loại bỏ chúng ra.

Ngoài ra, tôi thích một giải pháp Java tiêu chuẩn không yêu cầu thư viện của bên thứ ba.

+0

Ben, bạn có thể cung cấp một số ví dụ không? – OscarRyz

+0

Đã thêm nhận xét đánh dấu câu hỏi vào câu hỏi của tôi. –

Trả lời

4

Nó không rõ ràng từ câu hỏi của bạn, nhưng vì bạn đang có kế hoạch chấp nhận tên đường dẫn từ một biểu mẫu web (?) Bạn có thể nên chặn các nỗ lực đổi tên một số thứ nhất định; ví dụ. "C: \ Program Files". Điều này ngụ ý rằng bạn cần phải hợp quy hóa các tên đường dẫn để loại bỏ "." và ".." trước khi bạn kiểm tra quyền truy cập của mình.

Cho rằng, tôi sẽ không cố gắng xóa các ký tự không hợp lệ. Thay vào đó, tôi sẽ sử dụng "File mới (str) .getCanonicalFile()" để tạo ra các đường dẫn kinh điển, kiểm tra tiếp theo rằng chúng đáp ứng các hạn chế sandboxing của bạn và cuối cùng sử dụng "File.exists()", "File.isFile()" , vv để kiểm tra xem nguồn và đích là kosher, và không phải là cùng một đối tượng hệ thống tập tin. Tôi sẽ đối phó với các nhân vật bất hợp pháp bằng cách cố gắng thực hiện các hoạt động và bắt các ngoại lệ.

24

Như được đề xuất ở nơi khác, điều này thường không phải là những gì bạn muốn làm. Nó thường là tốt nhất để tạo ra một tập tin tạm thời bằng cách sử dụng một phương pháp an toàn như File.createTempFile().

Bạn không nên làm điều này với danh sách cho phép và chỉ giữ các ký tự 'tốt'. Nếu tập tin được tạo thành chỉ có các ký tự Trung Quốc thì bạn sẽ loại bỏ mọi thứ ra khỏi nó. Chúng tôi không thể sử dụng danh sách trắng vì lý do này, chúng tôi phải sử dụng danh sách đen.

Linux khá nhiều cho phép bất cứ điều gì có thể là một nỗi đau thực sự. Tôi sẽ chỉ giới hạn Linux vào cùng một danh sách mà bạn giới hạn Windows để bạn tiết kiệm cho mình những cơn đau đầu trong tương lai.

Sử dụng đoạn mã C# này trên Windows Tôi đã tạo danh sách các ký tự không hợp lệ trên Windows. Có một vài nhân vật trong danh sách này nhiều hơn bạn có thể nghĩ (41) vì vậy tôi sẽ không khuyên bạn nên cố gắng tạo danh sách của riêng bạn.

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

Đây là một lớp Java đơn giản 'làm sạch' tên tệp.

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(); 
} 
} 

EDIT: Vì Stephen đề xuất bạn cũng nên xác minh rằng các truy cập tệp này chỉ xảy ra trong thư mục bạn cho phép.

Câu trả lời sau có mã mẫu để thiết lập ngữ cảnh bảo mật tùy chỉnh trong Java và sau đó thực thi mã trong 'sandbox' đó.

How do you create a secure JEXL (scripting) sandbox?

+0

Ví dụ java tốt, nhưng tại sao bạn không bao gồm dấu gạch chéo (47)? – THelper

+0

Không biết tại sao nó không có trong danh sách. Chúng tôi thực sự chỉ gặp phải vấn đề này trong mã sản xuất. Tôi đã sửa câu trả lời để bao gồm 47. Cảm ơn. –

+0

Câu trả lời thú vị. thanks – Ponmalar

6

Có một tốt đẹp tích hợp giải pháp Java - Character.isXxx().

Hãy thử 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); 
    } 
} 

Kết quả là "name.é $ _".

+0

okay, vì vậy nó là một cách bảo thủ và không đáp ứng đầy đủ câu hỏi gốc (cross-platform), nhưng làm việc cho tôi :) –

+3

Nó loại bỏ dấu gạch ngang có giá trị cho tên tệp (ít nhất là trong Windows) nhưng nó thực hiện công việc , dù sao tôi nghĩ Apache Commons FilenameUtils nên kết hợp một cách nền tảng chéo để có được điều này thực hiện –

+0

cũng nó loại bỏ "@" quá đó là một lần nữa hợp lệ trong Windows. – azerafati

17

hoặc chỉ làm điều này:

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

Kết quả: A20_B22b_A_BC_ld_ma.la.xps

Giải thích:

[a-zA-Z0-9\\._] phù hợp với một lá thư từ az thường hoặc in hoa, chữ số, dấu chấm và dấu gạch dưới

[^a-zA-Z0-9\\._] là nghịch đảo. ví dụ: tất cả các nhân vật mà không phù hợp với biểu hiện đầu tiên

[^a-zA-Z0-9\\._]+ là một chuỗi các ký tự mà không phù hợp với biểu hiện đầu tiên

Vì vậy, mỗi chuỗi các ký tự mà không bao gồm các ký tự từ a-z, 0-9 hay. _ sẽ được thay thế.

+4

Điều này hoạt động trên tên tệp chỉ sử dụng chữ cái tiếng Anh. Nếu tập tin được tạo thành chỉ có các ký tự Trung Quốc thì bạn sẽ loại bỏ mọi thứ ra khỏi nó. Chúng tôi không thể sử dụng danh sách trắng trên các chuỗi để tách các ký tự xấu vì lý do này, chúng tôi phải sử dụng danh sách đen. –

+0

Có một cái nhìn ở đây: http://stackoverflow.com/questions/9576384/use-regular-expression-to-match-any-chinese-character-in-utf-8-encoding nó sẽ hoạt động nếu bạn sử dụng Java 7 – Dirk

+0

@Dirk Downvoted vì regex không phải là giải pháp ở đây. Nếu tên tệp bằng nhiều ngôn ngữ thì sao? –

5

Đây là mã tôi sử dụng:

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(); 
} 

SystemUtils là từ Apache commons-lang3

+0

không có SystemUtils: if (File.separatorChar == '/') { tên trả về.replaceAll ("/ +", "") .trim(); } –

9

này được dựa trên câu trả lời chấp nhận bởi Sarel Botha mà hoạt động tốt miễn là bạn không gặp phải bất kỳ ký tự bên ngoài số Basic Multilingual Plane. Nếu bạn cần hỗ trợ Unicode đầy đủ (? Và ai không) sử dụng mã này để thay thế đó là Unicode an toàn:

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(); 
    } 
} 

thay đổi chính ở đây:

  • Sử dụng codePointCount i.c.w. length thay vì chỉ length
  • sử dụng codePointAt thay vì charAt
  • sử dụng appendCodePoint thay vì append
  • Không cần phải đúc char s để int s. Trong thực tế, bạn không bao giờ nên đối phó với char s vì chúng về cơ bản bị hỏng cho bất cứ điều gì bên ngoài BMP.
+0

Bạn có thể sử dụng các chức năng tiêu chuẩn và làm việc với ký tự - bạn chỉ cần bỏ qua ký tự theo sau cặp ký tự thay thế. Cũng chars không bao giờ cần phải được đúc thành các loại số - chúng là số theo thiết kế. – nekavally

0

Nếu bạn muốn sử dụng nhiều hơn [A-Za-z0-9], hãy kiểm tra MS Naming Conventions và đừng quên lọc "... Các ký tự có đại diện số nguyên nằm trong khoảng từ 1 đến 31 , ... ", giống như ví dụ của Aaron Digulla. Mã ví dụ: từ David Carboni sẽ không đủ cho những ký tự này.

Các vấn đề liên quan