ফাইল নাম হিসাবে ব্যবহার করার জন্য আমি কীভাবে জাভাতে একটি স্ট্রিং নিরাপদে এনকোড করতে পারি?


117

আমি একটি বাহ্যিক প্রক্রিয়া থেকে একটি স্ট্রিং গ্রহণ করছি। আমি ফাইলের নাম তৈরি করতে সেই স্ট্রিংটি ব্যবহার করতে চাই এবং তারপরে সেই ফাইলটিতে লিখি। এটি করার জন্য আমার কোড স্নিপেট এখানে:

    String s = ... // comes from external source
    File currentFile = new File(System.getProperty("user.home"), s);
    PrintWriter currentWriter = new PrintWriter(currentFile);

S এর যদি একটি অবৈধ অক্ষর থাকে, যেমন ইউনিক্স-ভিত্তিক ওএসে '/', তবে একটি java.io.FileNotFoundException (সঠিকভাবে) নিক্ষেপ করা হয়।

আমি কীভাবে স্ট্রিংটিকে নিরাপদে এনকোড করব যাতে এটি ফাইলের নাম হিসাবে ব্যবহার করা যায়?

সম্পাদনা: আমি যা প্রত্যাশা করছি তা হ'ল একটি এপিআই কল যা আমার জন্য এটি করে।

আমি এটা করতে পারবো:

    String s = ... // comes from external source
    File currentFile = new File(System.getProperty("user.home"), URLEncoder.encode(s, "UTF-8"));
    PrintWriter currentWriter = new PrintWriter(currentFile);

তবে আমি নিশ্চিত না যে এই উদ্দেশ্যে ইউআরএলএনসি কোডারটি নির্ভরযোগ্য কিনা।


1
স্ট্রিংটি এনকোড করার উদ্দেশ্য কী?
স্টিফেন সি

3
@ স্টেফেন সি: স্ট্রিংটির এনকোডিংয়ের উদ্দেশ্য হ'ল ফাইল নাম হিসাবে উপযুক্ত হিসাবে তৈরি করা, যেমন জাভা ডট কম UR
স্টিভ ম্যাকলিউড

1
আচ্ছা বুঝলাম. এনকোডিংটি কি বিপরীত হওয়া দরকার?
স্টিফেন সি

@ স্টেফেন সি: না, এটির বিপরীতমুখী হওয়ার দরকার নেই, তবে আমি ফলাফলটি মূল স্ট্রিংয়ের যতটা সম্ভব তার সাথে সাদৃশ্যযুক্ত হওয়া চাই।
স্টিভ ম্যাকলিউড

1
এনকোডিংয়ের কি আসল নামটি অস্পষ্ট করা দরকার? এটি কি 1-থেকে -1 হওয়া প্রয়োজন; অর্থাৎ সংঘর্ষ ঠিক আছে?
স্টিফেন সি

উত্তর:


17

আপনি যদি ফলাফলটি মূল ফাইলটির সাথে সাদৃশ্য করতে চান তবে SHA-1 বা অন্য কোনও হ্যাশিং স্কিম উত্তর নয়। যদি সংঘর্ষগুলি এড়াতে হবে, তবে সাধারণ বদলি বা "খারাপ" চরিত্রগুলি অপসারণও উত্তর নয় either

পরিবর্তে আপনি এই জাতীয় কিছু চান। (দ্রষ্টব্য: এটি চিত্রের উদাহরণ হিসাবে বিবেচনা করা উচিত, অনুলিপি এবং পেস্ট করার মতো কিছু নয়))

char fileSep = '/'; // ... or do this portably.
char escape = '%'; // ... or some other legal char.
String s = ...
int len = s.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
    char ch = s.charAt(i);
    if (ch < ' ' || ch >= 0x7F || ch == fileSep || ... // add other illegal chars
        || (ch == '.' && i == 0) // we don't want to collide with "." or ".."!
        || ch == escape) {
        sb.append(escape);
        if (ch < 0x10) {
            sb.append('0');
        }
        sb.append(Integer.toHexString(ch));
    } else {
        sb.append(ch);
    }
}
File currentFile = new File(System.getProperty("user.home"), sb.toString());
PrintWriter currentWriter = new PrintWriter(currentFile);

এই সমাধানটি একটি বিপরীতমুখী এনকোডিং দেয় (কোনও সংঘর্ষ ছাড়াই) যেখানে এনকোডযুক্ত স্ট্রিং বেশিরভাগ ক্ষেত্রে মূল স্ট্রিংয়ের সাথে সাদৃশ্যপূর্ণ। আমি ধরে নিচ্ছি যে আপনি 8-বিট অক্ষর ব্যবহার করছেন।

URLEncoder কাজ করে, তবে এর অসুবিধাটি এটি আইনী ফাইলের নামের পুরো অক্ষরকে পুরোটা এনকোড করে।

যদি আপনি কোনও গ্যারান্টিযুক্ত-না-ফেরানোযোগ্য সমাধান চান তবে কেবল 'খারাপ' অক্ষরগুলি পালানোর ক্রমগুলির পরিবর্তে পরিবর্ত করুন।


উপরের এনকোডিংয়ের বিপরীতটি কার্যকর করতে সমান সোজা-এগিয়ে হওয়া উচিত।


105

আমার পরামর্শ হ'ল "সাদা তালিকা" পদ্ধতির গ্রহণ করা, যার অর্থ খারাপ চরিত্রগুলিকে চেষ্টা এবং ফিল্টার করা উচিত নয়। পরিবর্তে যা ঠিক আছে তা সংজ্ঞায়িত করুন। আপনি হয় ফাইলের নাম প্রত্যাখ্যান করতে পারেন বা ফিল্টার করতে পারেন। আপনি যদি এটি ফিল্টার করতে চান:

String name = s.replaceAll("\\W+", "");

কি এই আছে কোন চরিত্র প্রতিস্থাপন নয় কিছুই একটি নম্বর চিঠি বা আন্ডারস্কোর। বিকল্পভাবে আপনি তাদের অন্য একটি চরিত্রের সাথে প্রতিস্থাপন করতে পারেন (আন্ডারস্কোরের মতো)।

সমস্যাটি হ'ল যদি এটি একটি ভাগ করা ডিরেক্টরি হয় তবে আপনি ফাইলের নাম সংঘর্ষ চান না। এমনকি যদি ব্যবহারকারী দ্বারা স্টোরেজ অঞ্চলগুলি পৃথক করা হয় তবে আপনি কেবল খারাপ চরিত্রগুলি ফিল্টার করে একটি সংঘর্ষের ফাইলের নাম দিয়ে শেষ করতে পারেন। ব্যবহারকারীরা যে নামটি রেখেছেন তা প্রায়শই কার্যকর হয় যদি তারা কখনও এটি ডাউনলোড করতে চায়।

এই কারণে আমি ব্যবহারকারীরা তাদের যা চান তা প্রবেশ করতে দেয়, আমার নিজের পছন্দসই স্কিমের উপর ভিত্তি করে ফাইলের নাম সংরক্ষণ করে (যেমন ইউজারআইডি_ফিলিআইডি) এবং তারপরে একটি ডাটাবেস সারণিতে ব্যবহারকারীর ফাইল নাম সংরক্ষণ করে। এইভাবে আপনি এটি ব্যবহারকারীর কাছে ফিরিয়ে প্রদর্শন করতে পারেন, কীভাবে জিনিস আপনি চান তা সঞ্চয় করতে পারেন এবং আপনি সুরক্ষার সাথে কোনও আপস করবেন না বা অন্য ফাইলগুলি মুছবেন না।

আপনি ফাইলটি (যেমন MD5 হ্যাশ) হ্যাশ করতে পারেন তবে তারপরে ব্যবহারকারীরা যে ফাইলগুলি রেখেছেন সেগুলি তালিকাভুক্ত করতে পারবেন না (কোনও অর্থবহ নাম দিয়ে নয়)।

সম্পাদনা: জাভা জন্য স্থির রেজেক্স


আমি প্রথমে খারাপ সমাধান সরবরাহ করা ভাল ধারণা বলে মনে করি না। এছাড়াও, MD5 হল প্রায় ক্র্যাক হ্যাশ অ্যালগরিদম। আমি কমপক্ষে SHA-1 বা আরও ভাল প্রস্তাব দিচ্ছি।
vog

19
একটি অনন্য ফাইল নাম তৈরি করার উদ্দেশ্যে অ্যালগরিদম "ভাঙ্গা" থাকলে কে কে যত্ন করে?
ক্লিটাস

3
@ ক্লেটাস: সমস্যাটি হ'ল বিভিন্ন স্ট্রিং একই ফাইলনামে ম্যাপ করবে; অর্থাৎ সংঘর্ষ।
স্টিফেন সি

3
সংঘর্ষটি ইচ্ছাকৃতভাবে হতে হবে, আসল প্রশ্নটি আক্রমণকারী দ্বারা নির্বাচিত এই স্ট্রিংগুলির বিষয়ে কথা বলবে না।
টিয়ালারামেক্স

8
আপনার "\\W+"জাভাতে regexp ব্যবহারের প্রয়োজন । ব্যাকস্ল্যাশ প্রথমে স্ট্রিংয়ের জন্য প্রযোজ্য এবং \Wএটি কোনও বৈধ পালানোর ক্রম নয়। আমি উত্তরটি সম্পাদনা করার চেষ্টা করেছি, তবে দেখে মনে হচ্ছে কেউ আমার সম্পাদনাটি প্রত্যাখ্যান করেছে :(
ভাদ্পিপ

35

এটি এনকোডিংটি বিপরীত হওয়া উচিত কিনা তার উপর নির্ভর করে।

উলটাকর

এর java.net.URLEncoderসাথে বিশেষ অক্ষরগুলি প্রতিস্থাপন করতে URL এনকোডিং ( ) ব্যবহার করুন %xx। নোট করুন যে আপনি বিশেষ ক্ষেত্রে যেখানে স্ট্রিং সমান ., সমান যত্ন নেবেন.. বা খালি! Is অনেক প্রোগ্রাম ফাইলের নাম তৈরি করতে ইউআরএল এনকোডিং ব্যবহার করে, সুতরাং এটি একটি স্ট্যান্ডার্ড কৌশল যা প্রত্যেকে বোঝে।

অপরিবর্তনীয়

প্রদত্ত স্ট্রিংয়ের একটি হ্যাশ (যেমন SHA-1) ব্যবহার করুন। আধুনিক হ্যাশ অ্যালগরিদমগুলি ( এমডি 5 নয় ) সংঘর্ষ-মুক্ত হিসাবে বিবেচনা করা যেতে পারে। আসলে, যদি আপনি কোনও সংঘর্ষের সন্ধান পান তবে ক্রিপ্টোগ্রাফিতে একটি ব্রেক-থ্রো পাবেন।


¹ আপনি যেমন 3 টির মতো একটি উপসর্গ ব্যবহার করে মার্জিতভাবে সমস্ত 3 বিশেষ কেস পরিচালনা করতে পারেন "myApp-"। আপনি যদি ফাইলটি সরাসরি এতে প্রবেশ $HOMEকরেন তবে ".bashrc" এর মতো বিদ্যমান ফাইলগুলির সাথে বিরোধগুলি এড়াতে আপনাকে যেভাবেই করতে হবে।
public static String encodeFilename(String s)
{
    try
    {
        return "myApp-" + java.net.URLEncoder.encode(s, "UTF-8");
    }
    catch (java.io.UnsupportedEncodingException e)
    {
        throw new RuntimeException("UTF-8 is an unknown encoding!?");
    }
}


2
বিশেষ চরিত্রটি কী তা সম্পর্কে ইউআরএলএনকোডার ধারণাটি সঠিক নাও হতে পারে।
স্টিফেন সি

4
@ ভোগ: ইউআরএলএনসি কোডার "" এর জন্য ব্যর্থ। এবং "..". এগুলি অবশ্যই এনকোড করা উচিত নয়ত আপনি ent হোম
স্টিফেন সি-তে

6
@vog: "*" শুধুমাত্র বেশিরভাগ ইউনিক্স-ভিত্তিক ফাইল সিস্টেমগুলিতে অনুমোদিত, এনটিএফএস এবং এফএটি 32 এটি সমর্থন করে না।
জোনাথন

1
"।" এবং ".." বিন্দুগুলি% 2E এ পালানোর মাধ্যমে মোকাবিলা করা যেতে পারে যখন স্ট্রিংটি কেবল বিন্দুতে থাকে (যদি আপনি পালানোর ক্রমটি ছোট করতে চান)। '*' এছাড়াও "% 2A" দ্বারা প্রতিস্থাপিত হতে পারে।
ভিফ

1
নোট করুন যে ফাইলের নাম দীর্ঘায়িত করে এমন কোনও পন্থা (একক অক্ষর% 20 বা যেকোনো
কিছুতে পরিবর্তন করে

24

আমি যা ব্যবহার করি তা এখানে:

public String sanitizeFilename(String inputName) {
    return inputName.replaceAll("[^a-zA-Z0-9-_\\.]", "_");
}

এটি যা করে তা হ'ল প্রতিটি চরিত্রকে প্রতিস্থাপন করা যা কোনও অক্ষর, সংখ্যা, আন্ডারস্কোর বা বিন্দু নয় আন্ডারস্কোর দিয়ে বিন্দু ব্যবহার করে ge

এর অর্থ হ'ল "কীভাবে £ কে $ রূপান্তর করবেন" এর মতো কিছু "How_to_convers___to__" তে পরিণত হবে। স্বীকারযোগ্যভাবে, এই ফলাফলটি খুব ব্যবহারকারী-বান্ধব নয়, তবে এটি নিরাপদ এবং ফলস্বরূপ ডিরেক্টরি / ফাইলের নাম সর্বত্র কাজ করার গ্যারান্টিযুক্ত। আমার ক্ষেত্রে, ফলাফলটি ব্যবহারকারীকে দেখানো হয় না, এবং এটি কোনও সমস্যা নয়, তবে আপনি আরও কার্যকর হতে রেজেক্সকে পরিবর্তন করতে চাইতে পারেন।

অন্য যে সমস্যার মুখোমুখি হয়েছি তা হ'ল আমি মাঝে মাঝে অভিন্ন নাম (যেহেতু এটি ব্যবহারকারীর ইনপুট উপর ভিত্তি করে) পাওয়া যায়, তাই আপনার সচেতন হওয়া উচিত, যেহেতু আপনার একক ডিরেক্টরিতে একই নামের একাধিক ডিরেক্টরি / ফাইল থাকতে পারে না । আমি কেবল বর্তমান সময় এবং তারিখ এবং এটি এড়াতে একটি সংক্ষিপ্ত এলোমেলো স্ট্রিং চাপিয়েছি। (প্রকৃত এলোমেলো স্ট্রিং, ফাইলনামের একটি হ্যাশ নয়, কারণ অভিন্ন ফাইলের নামগুলির ফলে অভিন্ন হ্যাশগুলি হবে)

এছাড়াও, আপনাকে ফলস্বরূপ স্ট্রিংটি কেটে বা অন্যথায় ছোট করতে হবে, কারণ এটি কিছু সিস্টেমের 255 অক্ষরের সীমা অতিক্রম করতে পারে।


6
আরেকটি সমস্যা হ'ল এটি এএসসিআইআই অক্ষর ব্যবহার করে এমন ভাষার সাথে নির্দিষ্ট। অন্যান্য ভাষার ক্ষেত্রে এটির ফলে ফাইলের নামগুলি আন্ডারস্কোর ছাড়া কিছুই থাকবে না।
অ্যান্ডি টমাস

13

যারা সাধারণ সমাধান খুঁজছেন তাদের ক্ষেত্রে এগুলি সাধারণ ক্রাইটার হতে পারে:

  • ফাইলের নামটি স্ট্রিংয়ের অনুরূপ হওয়া উচিত।
  • যেখানে সম্ভব সম্ভব এনকোডিংটি পুনরায় পরিবর্তনযোগ্য হওয়া উচিত।
  • সংঘর্ষের সম্ভাবনা হ্রাস করা উচিত।

এটি অর্জনের জন্য আমরা অবৈধ অক্ষরগুলির সাথে মেলে রেজেক্স ব্যবহার করতে পারি, সেগুলি শতাংশ-এনকোড করতে পারি, তারপরে এনকোডযুক্ত স্ট্রিংয়ের দৈর্ঘ্য সীমাবদ্ধ করি।

private static final Pattern PATTERN = Pattern.compile("[^A-Za-z0-9_\\-]");

private static final int MAX_LENGTH = 127;

public static String escapeStringAsFilename(String in){

    StringBuffer sb = new StringBuffer();

    // Apply the regex.
    Matcher m = PATTERN.matcher(in);

    while (m.find()) {

        // Convert matched character to percent-encoded.
        String replacement = "%"+Integer.toHexString(m.group().charAt(0)).toUpperCase();

        m.appendReplacement(sb,replacement);
    }
    m.appendTail(sb);

    String encoded = sb.toString();

    // Truncate the string.
    int end = Math.min(encoded.length(),MAX_LENGTH);
    return encoded.substring(0,end);
}

প্যাটার্নস

উপরের প্যাটার্নটি পসিক্স অনুচ্ছেদে অনুমোদিত অক্ষরের একটি রক্ষণশীল সাবসেটের উপর ভিত্তি করে ।

আপনি যদি বিন্দু চরিত্রটিকে অনুমতি দিতে চান তবে ব্যবহার করুন:

private static final Pattern PATTERN = Pattern.compile("[^A-Za-z0-9_\\-\\.]");

"" এর মতো স্ট্রিং থেকে সাবধান থাকুন। এবং ".."

যদি আপনি সংবেদনশীল ফাইল সিস্টেমের সংঘর্ষগুলি এড়াতে চান তবে আপনাকে রাজধানী থেকে বাঁচতে হবে:

private static final Pattern PATTERN = Pattern.compile("[^a-z0-9_\\-]");

বা ছোট হাতের অক্ষরগুলি এড়িয়ে চলুন:

private static final Pattern PATTERN = Pattern.compile("[^A-Z0-9_\\-]");

একটি শ্বেতলিস্ট ব্যবহার না করে, আপনি আপনার নির্দিষ্ট ফাইল সিস্টেমের জন্য সংরক্ষিত অক্ষরগুলি কালো তালিকাভুক্ত করতে পারেন। EG এই রেজেক্স FAT32 ফাইল সিস্টেমে স্যুট করে:

private static final Pattern PATTERN = Pattern.compile("[%\\.\"\\*/:<>\\?\\\\\\|\\+,\\.;=\\[\\]]");

লম্বা

অ্যান্ড্রয়েডে, 127 টি অক্ষর নিরাপদ সীমা। অনেক ফাইল সিস্টেম 255 টি অক্ষরের অনুমতি দেয়।

আপনি যদি আপনার স্ট্রিংয়ের মাথার চেয়ে লেজ ধরে রাখতে চান তবে ব্যবহার করুন:

// Truncate the string.
int start = Math.max(0,encoded.length()-MAX_LENGTH);
return encoded.substring(start,encoded.length());

গঠনের কথা মাথায় রেখে

ফাইলের নামটি আবার মূল স্ট্রিংয়ে রূপান্তর করতে, ব্যবহার করুন:

URLDecoder.decode(filename, "UTF-8");

সীমাবদ্ধতা

যেহেতু দীর্ঘতর স্ট্রিংগুলি কেটে গেছে, এনকোডিং করার সময় নামের সংঘর্ষ বা ডিকোডিংয়ের সময় দুর্নীতি হওয়ার সম্ভাবনা রয়েছে।


1
পিক্সিক্স হাইফেনকে অনুমতি দেয় - আপনার এটি প্যাটার্নে যুক্ত করা উচিত -Pattern.compile("[^A-Za-z0-9_\\-]")
এমকেদেব

হাইফেন যুক্ত হয়েছে। ধন্যবাদ :)
শার্ক অলি

আমি মনে করি না যে শতাংশ-এনকোডিং উইন্ডোজগুলিতে সদয়ভাবে কাজ করবে, এটি একটি সংরক্ষিত চরিত্রের কারণে ..
অমলগোভিনাস

1
অ-ইংরাজী ভাষা বিবেচনা করে না।
নাটস

5

নিম্নলিখিত রেজেেক্স ব্যবহার করে চেষ্টা করুন যা প্রতিটি অবৈধ ফাইল নামের অক্ষরকে একটি জায়গার সাথে প্রতিস্থাপন করে:

public static String toValidFileName(String input)
{
    return input.replaceAll("[:\\\\/*\"?|<>']", " ");
}

স্পেসগুলি সিএলআই-এর জন্য কদর্য; _বা সঙ্গে প্রতিস্থাপন বিবেচনা করুন -
sdgfsdh


2

এটি সম্ভবত সবচেয়ে কার্যকর উপায় নয় তবে জাভা 8 পাইপলাইন ব্যবহার করে এটি কীভাবে করবেন তা দেখায়:

private static String sanitizeFileName(String name) {
    return name
            .chars()
            .mapToObj(i -> (char) i)
            .map(c -> Character.isWhitespace(c) ? '_' : c)
            .filter(c -> Character.isLetterOrDigit(c) || c == '-' || c == '_')
            .map(String::valueOf)
            .collect(Collectors.joining());
}

স্ট্রিংবিল্ডার ব্যবহার করে কাস্টম সংগ্রাহক তৈরি করে সমাধানটি আরও উন্নত হতে পারে, সুতরাং আপনাকে প্রতিটি হালকা ওজনের চরিত্রটিকে ভারী ওজনের স্ট্রিংয়ে কাস্ট করতে হবে না।


-1

আপনি অবৈধ অক্ষর ('/', '\', '?', '*') মুছে ফেলতে এবং তারপরে এটি ব্যবহার করতে পারেন।


1
এটি নামকরণের বিরোধের সম্ভাবনাটি প্রবর্তন করবে। উদাহরণস্বরূপ, "টেস? টি", "টেস * টি" এবং "টেস্ট" একই ফাইলটিতে "পরীক্ষা" যেতে পারে।
vog

সত্য। তারপরে তাদের প্রতিস্থাপন করুন। উদাহরণস্বরূপ, '/' -> স্ল্যাশ, '*' -> তারা ... বা ভোগের পরামর্শ অনুসারে একটি হ্যাশ ব্যবহার করুন।
বুখার্ড

4
আপনি বিরোধের নামকরণের সম্ভাবনার জন্য সর্বদা উন্মুক্ত রয়েছেন
ব্রায়ান অগ্নিউ

2
"?" এবং "*" ফাইলের নামগুলিতে অনুমোদিত অক্ষর। তাদের কেবল শেল কমান্ডে পালাতে হবে, কারণ সাধারণত গ্লোব্বিং ব্যবহার করা হয়। ফাইলের এপিআই স্তরে অবশ্য কোনও সমস্যা নেই।
vog

2
@ ব্রায়ান অগ্নিউ: আসলে সত্য নয়। বিপরীতযোগ্য পালানোর স্কিম ব্যবহার করে অবৈধ অক্ষরগুলিকে এনকোড করে এমন স্কিমগুলি সংঘর্ষ দেয় না।
স্টিফেন সি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.