سؤال

أنا أعاني من مشكلة ترميز اسم ملف غريب عند إدراج محتويات الدليل في Java 6 على كل من OS X و Linux: File.listFiles() ويبدو أن الأساليب ذات الصلة تُرجع أسماء الملفات في ترميز مختلف عن بقية النظام.

لاحظ أنه ليس مجرد عرض أسماء الملفات هذه التي تسبب لي مشاكل. أنا مهتم بشكل أساسي بإجراء مقارنة لأسماء الملفات مع نظام تخزين الملفات عن بُعد ، لذلك أهتم أكثر بمحتوى سلاسل الأسماء أكثر من ترميز الأحرف المستخدمة لطباعة الإخراج.

هنا برنامج لإظهاره. يقوم بإنشاء ملف يحمل اسم Unicode ثم يطبع ترميز عناوين URL إصدارات أسماء الملفات التي تم الحصول عليها من الملف الذي تم إنشاؤه مباشرة ، ونفس الملف عند سرده ضمن دليل الأصل (يجب عليك تشغيل هذا الرمز في دليل فارغ). تظهر النتائج الترميز المختلفة التي تم إرجاعها بواسطة File.listFiles() طريقة.

String fileName = "Trîcky Nåme";
File file = new File(fileName);
file.createNewFile();
System.out.println("File name: " + URLEncoder.encode(file.getName(), "UTF-8"));

// Get parent (current) dir and list file contents
File parentDir = file.getAbsoluteFile().getParentFile();
File[] children = parentDir.listFiles();
for (File child: children) {
    System.out.println("Listed name: " + URLEncoder.encode(child.getName(), "UTF-8"));
}

إليك ما أحصل عليه عندما أقوم بتشغيل رمز الاختبار هذا على أنظامي. لاحظ ال %CC عكس %C3 تمثيلات الشخصية.

OS X Snow Leopard:

File name: Tri%CC%82cky+Na%CC%8Ame
Listed name: Tr%C3%AEcky+N%C3%A5me

$ java -version
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065)
Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01-279, mixed mode)

Kubuntu Linux (يعمل في VM على نفس نظام OS X):

File name: Tri%CC%82cky+Na%CC%8Ame
Listed name: Tr%C3%AEcky+N%C3%A5me

$ java -version
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1)
OpenJDK Client VM (build 16.0-b13, mixed mode, sharing)

لقد حاولت العديد من الاختراقات للحصول على الأوتار للاتفاق ، بما في ذلك تحديد file.encoding خاصية النظام ومختلف LC_CTYPE و LANG متغيرات البيئة. لا شيء يساعد ، ولا أريد اللجوء إلى مثل هذه الاختراقات.

على عكس هذا السؤال (مرتبط إلى حد ما؟), ، أنا قادر على قراءة البيانات من الملفات المدرجة على الرغم من الأسماء الفردية

هل كانت مفيدة؟

المحلول

باستخدام Unicode ، هناك أكثر من طريقة صالحة لتمثيل نفس الحرف. الشخصيات التي تستخدمها في اسمك الصعب هي "حرف صغير لاتيني أنا مع محيط" و "حرف صغير لاتيني A مع حلقة أعلاه".

أنت تقول "لاحظ %CC عكس %C3 تمثيلات الشخصية "، ولكن النظر عن كثب ما تراه هو التسلسل

i 0xCC 0x82 vs. 0xC3 0xAE
a 0xCC 0x8A vs. 0xC3 0xA5

هذا هو ، الأول هو رسالة i تليها 0xcc82 وهو ترميز UTF-8 من يونيكود\u0302 "الجمع بين اللكنة المحيطة" بينما الثاني هو UTF-8 ل \u00EE "رسالة صغيرة لاتينية أنا مع محيط". وبالمثل بالنسبة للزوج الآخر ، فإن الأول هو الرسالة a تليها 0xcc8a شخصية "الجمع بين الحلقة أعلاه" والثاني هو "الحرف الصغير اللاتيني A مع حلقة أعلاه". كلاهما هما ترميزات UTF-8 صالحة لسلاسل أحرف Unicode صالحة ، ولكن أحدهما في "مؤلف" والآخر بتنسيق "متحلل".

OS X HFS Plus Volumes Store Strings (على سبيل المثال أسماء الملفات) باعتبارها "متحللة بالكامل". يتم تخزين نظام الملفات UNIX حقًا وفقًا لكيفية اختيار برنامج تشغيل نظام الملفات لتخزينه. لا يمكنك الإدلاء بأي عبارات شاملة عبر أنواع مختلفة من أنظمة الملفات.

انظر مقال ويكيبيديا عن معادلة يونيكود للمناقشة العامة للأشكال المتحللة مقابل VS ، والتي تذكر OS X على وجه التحديد.

انظر أسئلة وأجوبة في شركة Apple QA1235 (في الهدف C للأسف) للحصول على معلومات حول تحويل النماذج.

أ موضوع البريد الإلكتروني الأخير في قائمة بريد Java-Dev من Apple ، يمكن أن تكون بعض المساعدة لك.

في الأساس ، تحتاج إلى تطبيع النموذج المتحلل إلى نموذج مؤلف قبل أن تتمكن من مقارنة الأوتار.

نصائح أخرى

الحل المستخرج من السؤال:

بفضل ستيفن P لوضعني على المسار الصحيح.

الإصلاح أولاً ، للصبر. إذا كنت تجمع مع Java 6 يمكنك استخدام java.text.normalizer الفصل لتطبيع السلاسل في شكل شائع من اختيارك ، على سبيل المثال

// Normalize to "Normalization Form Canonical Decomposition" (NFD)
protected String normalizeUnicode(String str) {
    Normalizer.Form form = Normalizer.Form.NFD;
    if (!Normalizer.isNormalized(str, form)) {
        return Normalizer.normalize(str, form);
    }
    return str;
}

حيث java.text.Normalizer متوفر فقط في Java 6 وبعد ذلك ، إذا كنت بحاجة إلى التجميع مع Java 5 ، فقد تضطر إلى اللجوء إلى sun.text.Normalizer التنفيذ وشيء من هذا القبيل الاختراق القائم على الانعكاس أنظر أيضا كيف تعمل هذه الوظيفة الطبيعية؟

هذا وحده يكفي بالنسبة لي لأقرر أنني لن أؤيد تجميع مشروعي مع Java 5: |

إليكم أشياء أخرى مثيرة للاهتمام تعلمتها في هذه المغامرة القاسية.

  • يحدث الارتباك بسبب أسماء الملفات في أحد أشكال التطبيع التي لا يمكن مقارنتها مباشرة: شكل تطبيع التحلل الكنسي (NFD) أو تكوين الكنسي التنسيق (NFC). السابق يميل إلى الحصول على رسائل ASCII تليها "المعدلات" لإضافة لهجات وما إلى ذلك ، في حين أن الأخير لديه فقط الشخصيات الممتدة مع عدم وجود شخصية ACSCII الرائدة. اقرأ صفحة Wiki Stephen P المراجع للحصول على تفسير أفضل.

  • توجد حرفية لسلسلة Unicode مثل الرمز الموجود في رمز المثال (وتلك التي تم استلامها عبر HTTP في تطبيقي الحقيقي) في نموذج NFD ، بينما يتم إرجاع أسماء الملفات بواسطة File.listFiles() الطريقة هي NFC. يوضح مثال المصغر التالي الاختلافات:

    String name = "Trîcky Nåme";
    System.out.println("Original name: " + URLEncoder.encode(name, "UTF-8"));
    System.out.println("NFC Normalized name: " + URLEncoder.encode(
        Normalizer.normalize(name, Normalizer.Form.NFC), "UTF-8"));
    System.out.println("NFD Normalized name: " + URLEncoder.encode(
        Normalizer.normalize(name, Normalizer.Form.NFD), "UTF-8"));
    

    انتاج:

    Original name: Tri%CC%82cky+Na%CC%8Ame
    NFC Normalized name: Tr%C3%AEcky+N%C3%A5me
    NFD Normalized name: Tri%CC%82cky+Na%CC%8Ame
    
  • إذا قمت ببناء أ File كائن باسم السلسلة ، File.getName() ستؤدي الطريقة إلى إرجاع الاسم بأي شكل من الأشكال التي قدمتها في الأصل. ومع ذلك ، إذا اتصلت File الطرق التي تكتشف الأسماء من تلقاء نفسها ، يبدو أنها تعيد الأسماء في نموذج NFC. هذا هو جوتشا سيئة. انها بالتأكيد gotchme.

  • وفقا للاقتباس أدناه وثائق Apple يتم تخزين أسماء الملفات في نموذج التحلل (NFD) على نظام الملفات HFS Plus:

    عند العمل داخل نظام التشغيل Mac OS ، ستجد نفسك باستخدام مزيج من Unicode مسبق ومتحلل. على سبيل المثال ، تقوم HFS Plus بتحويل جميع أسماء الملفات إلى تحلل Unicode ، في حين أن لوحات المفاتيح Macintosh تنتج عمومًا Unicode مسبقًا.

    لذلك File.listFiles() الطريقة المفيدة (؟) تحول أسماء الملفات إلى النموذج (Pre) المكون (NFC).

لقد رأيت شيئًا مشابهًا من قبل. الأشخاص الذين يقومون بتحميل الملفات من Mac إلى WebApp يستخدمون أسماء الملفات مع é.

أ) في نظام التشغيل الذي يكون char عاديًا e + "علامة على ´ المطبق على الشار السابق"

ب) في Windows ، إنه شار خاص: é

كلاهما يونيكود. لذلك ... أفهم أنك تمرر خيار (ب) لإنشاء ملف ، وفي وقت ما يقوم Mac OS بتحويله إلى خيار (A). ربما إذا وجدت مشكلة التمثيل المزدوج عبر الإنترنت ، فيمكنك الحصول على طريقة للتعامل مع كلتا الحالتين بنجاح.

أتمنى أن يساعد!

على نظام الملفات UNIX ، اسم الملف هو حقًا بايت منتهية الفرق []. لذلك يجب على وقت تشغيل Java إجراء تحويل من Java.lang.String إلى Byte [] أثناء عملية CreateNewFile (). يخضع تحويل char-to-bete للمحطة. لقد تم اختبار الإعداد LC_ALL إلى en_US.UTF-8 و en_US.ISO-8859-1 وحصلت على نتائج متماسكة. هذا مع Sun (... Oracle) Java 1.6.0_20. ومع ذلك ، ل LC_ALL=en_US.POSIX, ، النتيجه هي:

File name:   Tr%C3%AEcky+N%C3%A5me
Listed name: Tr%3Fcky+N%3Fme

3F هي علامة استفهام. يخبرني أن التحويل لم يكن ناجحًا للشخصية غير ASCII. ثم مرة أخرى ، كل شيء كما هو متوقع.

لكن السبب وراء اختلاف سلسلتك بسبب التكافؤ بين الحرف u00ee (أو C3 AE في UTF-8) والتسلسل i+ u0302 (69 CC 82 في UTF-8). u0302 عبارة عن علامة تجميعية (الجمع بين لهجة محيط). حدث نوع من التطبيع أثناء إنشاء الملف. لست متأكدًا مما إذا كان قد تم في وقت تشغيل Java أو نظام التشغيل.

ملاحظة: لقد أخذتني بعض الوقت لمعرفة ذلك لأن مقتطف الرمز الذي نشرته ليس لديه علامة تجمعية عارية ولكن الشخصية المكافئة î (على سبيل المثال \u00ee). يجب أن تكون قد قمت بتضمين تسلسل الهروب Unicode في السلسلة الحرفية (ولكن من السهل القول ذلك بعد ذلك ...).

أظن أنه عليك فقط أن تعود على الإرشاد javac ما هو الترميز لاستخدامه لتجميع .java ملف يحتوي على الأحرف الخاصة منذ أن قمت بتشفيره في الملف المصدر. وإلا فإن الترميز الافتراضي للمنصة سيتم استخدامه ، والذي قد لا يكون UTF-8 على الإطلاق.

يمكنك استخدام وسيطة VM -encoding لهذا.

javac -encoding UTF-8 com/example/Foo.java

بهذه الطريقة الناتجة .class سينتهي الملف يحتوي على الأحرف الصحيحة وستتمكن من إنشاء اسم الملف الصحيح وإدراجه أيضًا.

الحل البديل هو استخدام java.nio.path API بدلاً من java.io.file API الذي يعمل بشكل مثالي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top