أساسيات مجموعة الأحرف:
مجموعة الأحرف
مجموعة من الشخصيات، أي رموز ذات دلالات خاصة. الحرف "أ" هو حرف. "%" هو أيضًا حرف. ليس لها قيمة عددية جوهرية وليس لها اتصال مباشر بـ ASC II أو Unicode أو حتى أجهزة الكمبيوتر. كانت الرموز موجودة قبل وقت طويل من ظهور أجهزة الكمبيوتر.
مجموعة أحرف مشفرة
يتم تعيين قيمة رقمية لمجموعة من الأحرف. قم بتعيين رموز للأحرف حتى تتمكن من التعبير عن نتائج رقمية باستخدام مجموعة ترميز أحرف محددة. يمكن لمجموعات الأحرف المشفرة الأخرى تعيين قيم مختلفة لنفس الحرف. عادةً ما يتم تحديد تعيينات مجموعة الأحرف بواسطة مؤسسات المعايير، مثل USASCII وISO 8859-1 وUnicode (ISO 10646-1) وJIS X0201.
نظام ترميز الأحرف
تعيين أعضاء مجموعة الأحرف المشفرة إلى ثمانيات (بايت 8 بت). يحدد نظام الترميز كيفية التعبير عن سلسلة من ترميزات الأحرف كسلسلة من البايتات. لا يلزم أن تكون قيمة ترميز الأحرف هي نفس قيمة بايت التشفير، ولا يلزم أن تكون علاقة رأس برأس أو رأس بأطراف. من حيث المبدأ، يمكن تقريب ترميز وفك تشفير مجموعة الأحرف كتسلسل كائن وإلغاء تسلسل.
عادةً ما يتم استخدام ترميز بيانات الأحرف لنقل الشبكة أو تخزين الملفات. نظام الترميز ليس مجموعة أحرف، بل هو تعيين؛ ولكن بسبب العلاقة الوثيقة بينهما، ترتبط معظم الترميزات بمجموعة أحرف منفصلة. على سبيل المثال، UTF-8،
يُستخدم فقط لترميز مجموعات أحرف Unicode. ومع ذلك، من الممكن استخدام نظام ترميز واحد للتعامل مع مجموعات أحرف متعددة. على سبيل المثال، يمكن لـ EUC ترميز الأحرف للعديد من اللغات الآسيوية.
الشكل 6-1 عبارة عن تعبير رسومي يستخدم نظام تشفير UTF-8 لتشفير تسلسل أحرف Unicode في تسلسل بايت. يقوم UTF-8 بتشفير قيم رموز الأحرف الأقل من 0x80 في قيمة أحادية البايت (ASC II القياسي). يتم ترميز جميع أحرف Unicode الأخرى كتسلسلات متعددة البايتات تتراوح من 2 إلى 6 بايت (http://www.ietf.org/rfc/rfc2279.txt).
مجموعة الأحرف
تم تعريف مصطلح مجموعة الأحرف في RFC2278 (http://ietf.org/rfc/rfc2278.txt). إنها عبارة عن مجموعة من مجموعات الأحرف المشفرة وأنظمة ترميز الأحرف. فئة الحزمة java.nio.charset هي Charset، والتي تتضمن استخراج مجموعة الأحرف.
111111111111111
Unicode هو ترميز أحرف 16 بت. إنه يحاول توحيد مجموعات الأحرف لجميع لغات العالم في خريطة واحدة شاملة. لقد اكتسب مكانته، ولكن هناك العديد من ترميزات الأحرف الأخرى المستخدمة على نطاق واسع اليوم.
لا تزال معظم أنظمة التشغيل موجهة نحو البايت من حيث الإدخال/الإخراج وتخزين الملفات، لذلك بغض النظر عن التشفير المستخدم، Unicode أو الترميزات الأخرى، لا تزال هناك حاجة للتحويل بين تسلسلات البايت وترميزات مجموعة الأحرف.
تلبي الفئات المكونة من الحزمة java.nio.charset هذه الحاجة. ليست هذه هي المرة الأولى التي تتعامل فيها منصة Java مع ترميز مجموعة الأحرف، ولكنه الحل الأكثر منهجية وشمولاً ومرونة. توفر الحزمة java.nio.charset.spi واجهة توفير الخادم (SPI) بحيث يمكن توصيل أجهزة التشفير وأجهزة فك التشفير حسب الحاجة.
مجموعة الأحرف: يتم تحديد القيمة الافتراضية عند بدء تشغيل JVM وتعتمد على بيئة نظام التشغيل الأساسي والإعدادات المحلية و/أو تكوين JVM. إذا كنت بحاجة إلى مجموعة أحرف معينة، فإن الرهان الأكثر أمانًا هو تسميتها بشكل صريح. لا تفترض أن النشر الافتراضي هو نفس بيئة التطوير الخاصة بك. أسماء مجموعات الأحرف ليست حساسة لحالة الأحرف، أي أن الأحرف الكبيرة والأحرف الصغيرة تعتبر متماثلة عند مقارنة أسماء مجموعات الأحرف. تحتفظ هيئة أسماء الإنترنت المخصصة (IANA) بجميع أسماء مجموعات الأحرف المسجلة رسميًا.
يوضح المثال 6-1 كيفية ترجمة الأحرف إلى تسلسلات بايت باستخدام تطبيقات مجموعة الأحرف المختلفة.
مثال 6-1: استخدام ترميز مجموعة الأحرف القياسية
الحزمة com.ronsoft.books.nio.charset؛
import java.nio.charset.Charset;
import java.nio.ByteBuffer;
/**
* اختبار ترميز مجموعة الأحرف. قم بتشغيل نفس سلسلة الإدخال التي تحتوي على بعض
* أحرف غير ascii، من خلال العديد من برامج ترميز Charset وتفريغ الأحرف السداسية
* قيم تسلسل البايت الناتج.
*
* @ المؤلف رون هيتشنز ([email protected])
*/
اختبار التشفير للفئة العامة {
public static void main(String[] argv) يطرح الاستثناء {
// هذا هو تسلسل الأحرف المراد تشفيره
إدخال السلسلة = "/u00bfMa/u00f1ana؟";
// قائمة مجموعات الأحرف التي سيتم التشفير بها
String[] charsetNames = { "US-ASCII"، "ISO-8859-1"، "UTF-8"،
"UTF-16BE"، "UTF-16LE"، "UTF-16" //، "X-ROT13"
};
for (int i = 0; i < charsetNames.length; i++) {
doEncode(Charset.forName(charsetNames[i]), input);
}
}
/**
* بالنسبة لمجموعة أحرف معينة وسلسلة إدخال، قم بتشفير الأحرف وطباعة ملف
* ترميز البايت الناتج في شكل قابل للقراءة.
*/
doEncode الفراغ الثابت الخاص (Charset cs، String input) {
ByteBuffer bb = cs.encode(input);
System.out.println("مجموعة الأحرف:" + cs.name());
System.out.println("الإدخال:" + الإدخال);
System.out.println("مشفر:");
لـ (int i = 0; bb.hasRemaining(); i++) {
int b = bb.get();
int ival = ((int) b) & 0xff;
شار ج = (شار) إيفال؛
// حافظ على المحاذاة الجدولية جميلة
إذا (ط < 10)
System.out.print(" ");
// طباعة رقم الفهرس
System.out.print(" " + i + ": ");
// مخرجات منسقة بشكل أفضل ستأتي يومًا ما...
إذا (ival <16)
System.out.print("0");
// اطبع القيمة السداسية للبايت
System.out.print(Integer.toHexString(ival));
// إذا بدا أن البايت يمثل قيمة a
// حرف قابل للطباعة، لا يوجد ضمان
// سيكون ذلك.
إذا (Character.isWhitespace(c) || Character.isISOControl(c)) {
System.out.println("");
} آخر {
System.out.println("(" + c + ")");
}
}
System.out.println("");
}
}
مجموعة الأحرف: ISO-8859-1
الإدخال: ما ؟ آنا ؟
مشفرة:
0:20
1: فرنك بلجيكي (؟)
2: 4 د (م)
3:61(أ)
4: ف1 (؟)
5:61(أ)
6: 6ه (ن)
7:61(أ)
8: 3و (؟)
مجموعة الأحرف: UTF-8
الإدخال: ما ؟ آنا ؟
مشفرة:
0:20
1: ج2 (؟)
2: فرنك بلجيكي (؟)
3: 4 د (م)
4:61(أ)
5: ج3 (؟)
6: ب1 (±)
7:61(أ)
8: 6 هـ (ن)
9:61(أ)
10: 3و (؟)
مجموعة الأحرف: UTF-16BE
الإدخال: ما ؟ آنا ؟
مشفرة:
0:00
1:20
2:00
3: فرنك بلجيكي (؟)
4:00
5: 4 د (م)
6:00
7:61(أ)
8:00
9: ف1 (؟)
10:00
11:61(أ)
12:00
13: 6 هـ (ن)
14:00
15: 61 (أ)
16:00
17: 3و (؟)
مجموعة الأحرف: UTF-16LE
الإدخال: ما ؟ آنا ؟
مشفرة:
0:20
1:00
2: فرنك بلجيكي (؟)
3:00
4: 4 د (م)
5:00
6:61(أ)
7:00
8: ف1 (؟)
9:00
10:61(أ)
11:00
12: 6 هـ (ن)
13:00
14: 61 (أ)
15:00
16: 3و (؟)
17:00
مجموعة الأحرف: UTF-16
الإدخال: ما ؟ آنا ؟
مشفرة:
0: الحديد (؟)
1: وما يليها (؟)
2:00
3:20
4:00
5: فرنك بلجيكي (؟)
6:00
7: 4 د (م)
8:00
9:61(أ)
10:00
11: ف1 (؟)
12:00
13: 61 (أ)
14:00
15: 6 هـ (ن)
16:00
17: 61 (أ)
18:00
19: 3و (؟)
الحزمة java.nio.charset؛
تنفذ Charset فئة مجردة عامة قابلة للمقارنة
{
يتم دعم المنطق المنطقي الثابت العام (String charsetName)
مجموعة الأحرف الثابتة العامة forName (String charsetName)
SortedMap العامة الثابتة المتاحة ()
اسم السلسلة النهائية العامة ()
الأسماء المستعارة للمجموعة النهائية العامة ()
اسم عرض السلسلة العامة ()
اسم عرض السلسلة العامة (اللغة المحلية)
تم تسجيل القيمة المنطقية النهائية العامة ()
المنطقية العامة canEncode()
الملخص العام CharsetEncoder newEncoder();
تشفير ByteBuffer النهائي العام (CharBuffer cb)
تشفير ByteBuffer النهائي العام (String str)
الملخص العام CharsetDecoder newDecoder();
فك تشفير CharBuffer النهائي العام (ByteBuffer bb)
يحتوي الملخص المنطقي العام على (Charset cs)؛
القيمة المنطقية النهائية العامة تساوي (Object ob)
العام النهائي int CompareTo (Object ob)
hashCode النهائي العام ()
السلسلة النهائية العامة toString ()
}
في معظم الأحيان، يهتم بائعو JVM فقط بهذه القواعد. ومع ذلك، إذا كنت تخطط لاستخدام مجموعة الأحرف الخاصة بك كجزء من التطبيق الخاص بك، فسيكون من المفيد معرفة ما لا يجب عليك فعله. يجب عليك إرجاع خطأ لـ isRegistered() وتسمية مجموعة الأحرف الخاصة بك بدءًا من "X -".
مقارنة مجموعة الأحرف:
تنفذ Charset فئة مجردة عامة قابلة للمقارنة
{
// هذه قائمة جزئية لواجهة برمجة التطبيقات (API).
يحتوي الملخص المنطقي العام على (Charset cs)؛
القيمة المنطقية النهائية العامة تساوي (Object ob)
العام النهائي int CompareTo (Object ob)
hashCode النهائي العام ()
السلسلة النهائية العامة toString ()
}
أداة تشفير مجموعة الأحرف: تتكون مجموعة الأحرف من مجموعة أحرف مشفرة ونظام تشفير مرتبط. تطبق فئتا CharsetEncoder وCharsetDecoder أنظمة التحويل.
ملاحظة واحدة حول CharsetEncoder API: أولاً، كلما كان نموذج encode() أبسط، كان أكثر ملاءمة. يجمع تشفير CharBuffer الذي توفره في ByteBuffer المُعاد تخصيصه بين جميع الترميزات. هذه هي الطريقة الأخيرة التي يتم استدعاؤها عند استدعاء encode() مباشرة على فئة Charset.
التدفق السفلي
تجاوز
إدخال غير صحيح
شخصية غير قابلة للتعيين
أثناء التشفير، إذا واجه برنامج التشفير إدخالاً معيبًا أو غير قابل للتعيين، فسيتم إرجاع كائن نتيجة. يمكنك أيضًا اختبار الأحرف الفردية، أو تسلسلات الأحرف، لتحديد ما إذا كان من الممكن تشفيرها. إليك كيفية التحقق مما إذا كان التشفير ممكنًا:
الحزمة java.nio.charset؛
فئة مجردة عامة CharsetEncoder
{
// هذه قائمة جزئية لواجهة برمجة التطبيقات (API).
canEncode المنطقي العام (char c)
canEncode المنطقي العام (CharSequence cs)
}
تقرير
السلوك الافتراضي عند إنشاء CharsetEncoder. يشير هذا السلوك إلى أنه يجب الإبلاغ عن أخطاء الترميز عن طريق إرجاع كائن CoderResult المذكور سابقًا.
تجاهل (تجاهل)
يشير إلى أنه يجب تجاهل أخطاء الترميز ويجب إحباط أي إدخال غير صحيح إذا كان خارج موضعه.
يستبدل
تتم معالجة أخطاء الترميز عن طريق إلغاء إدخال الخطأ وإخراج تسلسل البايت البديل الحالي المحدد لـ CharsetEncoder هذا.
تذكر أن ترميز مجموعة الأحرف يحول الأحرف إلى سلسلة من البايتات استعدادًا لفك التشفير لاحقًا. إذا تعذر فك تشفير تسلسل الاستبدال إلى تسلسل أحرف صالح، يصبح تسلسل البايت المشفر غير صالح.
فئة CoderResult: يتم إرجاع كائنات CoderResult بواسطة كائنات CharsetEncoder وCharsetDecoder:
الحزمة java.nio.charset؛
الطبقة العامة CoderResult {
CoderResult OVERFLOW النهائي العام الثابت
CoderResult النهائية العامة الثابتة UNDERFLOW
المنطق المنطقي العام هو Underflow()
المنطقية العامة isOverflow()
<span style="white-space:pre"> </span>القيمة المنطقية العامة isError()
القيمة المنطقية العامة Malformed()
المنطقية العامة غير قابلة للتطبيق ()
طول int العام ()
CoderResult العام الثابت malformedForLength (طول int)
CoderResult العام الثابت unmappableForLength (طول int)
<span style="white-space:pre"> </span>الفراغ العام throwException() يرمي CharacterCodingException
}
الحزمة java.nio.charset؛
فئة مجردة عامة CharsetDecoder
{
// هذه قائمة جزئية لواجهة برمجة التطبيقات (API).
إعادة تعيين CharsetDecoder النهائية العامة ()
فك تشفير CharBuffer النهائي العام (ByteBuffer in)
يلقي CharacterCodingException
فك تشفير CoderResult النهائي العام (ByteBuffer in، CharBuffer out،
نهاية منطقية للإدخال)
تدفق CoderResult النهائي العام (خرج CharBuffer)
}
1. قم بإعادة ضبط وحدة فك التشفير عن طريق استدعاء إعادة التعيين () لوضع وحدة فك التشفير في حالة معروفة جاهزة لتلقي الإدخال.
2. اضبط endOfInput على false ولا تستدعي أو تستدعي decode() عدة مرات لتزويد محرك فك التشفير بالبايتات. مع استمرار عملية فك التشفير، ستتم إضافة الأحرف إلى CharBuffer المحدد.
3. اضبط endOfInput على true واتصل بـ decode() مرة واحدة لإعلام وحدة فك التشفير بأنه قد تم توفير كافة المدخلات.
4. قم باستدعاء Flush() للتأكد من إرسال كافة الأحرف التي تم فك تشفيرها إلى الإخراج.
يوضح المثال 6-2 كيفية تشفير دفق بايت يمثل ترميز مجموعة أحرف.
مثال 6-2. فك تشفير مجموعة الأحرف
الحزمة com.ronsoft.books.nio.charset؛
استيراد java.nio.*;
استيراد java.nio.charset.*;
استيراد java.nio.channels.*;
استيراد java.io.*;
/**
* اختبار فك تشفير مجموعة الأحرف.
*
* @ المؤلف رون هيتشنز ([email protected])
*/
الطبقة العامة CharsetDecode {
/**
* اختبار فك تشفير مجموعة الأحرف في الحالة العامة، واكتشاف المخزن المؤقت ومعالجته
* تحت / تجاوز السعة ومسح حالة وحدة فك التشفير في نهاية الإدخال
* يقرأ من stdin ويفك تشفير دفق البايت المشفر بـ ASCII إلى char
* تتم كتابة الأحرف التي تم فك تشفيرها إلى stdout وهذا يعد فعليًا "قطة" لـ
* إدخال ملفات ascii، ولكن يمكن استخدام ترميز محارف آخر ببساطة
* تحديده في سطر الأوامر.
*/
public static void main(String[] argv) يلقي IOException {
// مجموعة الأحرف الافتراضية هي ASCII القياسي
String charsetName = "ISO-8859-1";
// يمكن تحديد اسم مجموعة الأحرف في سطر الأوامر
إذا (argv. length > 0) {
charsetName = argv[0];
}
// لف قناة حول stdin، لف قناة حول stdout،
// ابحث عن مجموعة الأحرف المسماة وقم بتمريرها إلى طريقة deco de.
// إذا كانت مجموعة الأحرف المسماة غير صالحة، فسيتم استثناء النوع
// سيتم طرح UnsupportedCharsetException.
decodeChannel(Channels.newChannel(System.in)، OutputStreamWriter الجديد (
System.out)، Charset.forName(charsetName));
}
/**
* طريقة ثابتة للأغراض العامة تقرأ البايتات من القناة وتفك تشفيرها
* لهم وفقا
*
* @param المصدر
* كائن ReadableByteChannel والذي سيتم قراءته لـ EOF كـ a
* مصدر البايتات المشفرة.
* @param الكاتب
* كائن الكاتب الذي سيتم كتابة الأحرف التي تم فك تشفيرها.
* @param محارف
* كائن Charset، سيتم استخدام CharsetDecoder الخاص به للقيام بذلك
* فك تشفير مجموعة الأحرف Java NIO 206
*/
قناة decodeChannel العامة الثابتة (مصدر ReadableByteChannel، الكاتب الكاتب،
مجموعة محارف الأحرف) يطرح UnsupportedCharsetException، IOException {
// احصل على نسخة وحدة فك الترميز من مجموعة الأحرف
وحدة فك ترميز CharsetDecoder = charset.newDecoder();
// اطلب من وحدة فك الترميز استبدال الأحرف السيئة بالعلامة الافتراضية
decoder.onMalformedInput(CodingErrorAction.REPLACE);
decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
// تخصيص أحجام مختلفة جذريًا للمدخلات والمخرجات
// لأغراض الاختبار
ByteBuffer bb = ByteBuffer.allocateDirect(16 * 1024);
CharBuffer cb = CharBuffer.allocate(57);
// يبدأ المخزن المؤقت فارغًا للإشارة إلى الحاجة إلى الإدخال
نتيجة CoderResult = CoderResult.UNDERFLOW;
boolean eof = false;
بينما (!eof) {
// وحدة فك ترميز الإدخال تتطلب المزيد من المدخلات
إذا (النتيجة == CoderResult.UNDERFLOW) {
// وحدة فك الترميز تستهلك جميع المدخلات، وتستعد لإعادة التعبئة
bb.clear();
// املأ المخزن المؤقت للإدخال؛
eof = (source.read(bb) == -1);
// تحضير المخزن المؤقت للقراءة عن طريق وحدة فك الترميز
bb.flip();
}
// فك تشفير بايتات الإدخال لإخراج الأحرف؛
النتيجة = decoder.decode(bb, cb, eof);
// إذا كان المخزن المؤقت للإخراج ممتلئًا، فقم بتصريف الإخراج
إذا (النتيجة == CoderResult.OVERFLOW) {
rainCharBuf(cb,writer);
}
}
// امسح أي حالة متبقية من وحدة فك الترميز، مع توخي الحذر
// للكشف عن تجاوز سعة المخزن المؤقت للإخراج
بينما (decoder.flush(cb) == CoderResult.OVERFLOW) {
rainCharBuf(cb,writer);
}
// استنزاف أي أحرف متبقية في المخزن المؤقت للإخراج
rainCharBuf(cb,writer);
// أغلق القناة، ادفع أي بيانات مخزنة مؤقتًا إلى stdout
Source.Close();
الكاتب. فلوش ()؛
}
/**
* طريقة مساعدة لتصريف المخزن المؤقت char وكتابة محتواه إلى المعطى
* كائن الكاتب عند العودة، يكون المخزن المؤقت فارغًا وجاهزًا لإعادة تعبئته.
*
* @param سي بي
* يحتوي CharBuffer على أحرف ليتم كتابتها.
* @param الكاتب
* كائن كاتب يستهلك الأحرف الموجودة في cb.
*/
استنزاف الفراغ الثابتCharBuf (CharBuffer cb، الكاتب الكاتب) يلقي IOException {
cb.flip(); // تحضير المخزن المؤقت للتصريف
// هذا يكتب الأحرف الموجودة في CharBuffer ولكن
// لا يقوم فعليًا بتعديل حالة المخزن المؤقت.
// إذا تم استنزاف المخزن المؤقت للحرف عن طريق استدعاءات get( )،
// قد تكون هناك حاجة إلى حلقة هنا.
إذا (cb.hasRemaining()) {
الكاتب.write(cb.toString());
}
cb.clear(); // تحضير المخزن المؤقت ليتم ملؤه مرة أخرى
}
}
قبل تصفح واجهة برمجة التطبيقات (API)، من المهم شرح كيفية عمل Charset SPI. تحتوي الحزمة java.nio.charset.spi على فئة استخراج واحدة فقط، وهي CharsetProvider. توفر التطبيقات الملموسة لهذه الفئة معلومات تتعلق بكائنات Charset التي تقدمها. من أجل تحديد مجموعة أحرف مخصصة، يجب عليك أولاً إنشاء تطبيقات محددة لـ Charset وCharsetEncoder وCharsetDecoder من الحزمة java.nio.charset. يمكنك بعد ذلك إنشاء فئة فرعية مخصصة من CharsetProvider والتي ستوفر هذه الفئات إلى JVM.
إنشاء مجموعة أحرف مخصصة:
أقل ما عليك فعله هو إنشاء فئة فرعية من java.nio.charset.Charset، وتوفير تطبيقات ملموسة لطرق الاستخراج الثلاث، ومنشئ. لا تحتوي فئة Charset على مُنشئ افتراضي بدون معلمات. هذا يعني أن فئة مجموعة الأحرف المخصصة الخاصة بك يجب أن تحتوي على مُنشئ، حتى لو لم تقبل المعلمات. هذا لأنه يجب عليك الاتصال بمنشئ مجموعة الأحرف في وقت إنشاء مثيل (عن طريق الاتصال بـ super() في بداية مُنشئك)، وبالتالي تزويده باسم مواصفات مجموعة الأحرف والاسم المستعار. يؤدي القيام بذلك إلى السماح للطرق الموجودة في فئة Charset بمعالجة الأشياء المتعلقة بالاسم نيابةً عنك، وهذا أمر جيد.
وبالمثل، تحتاج إلى توفير تطبيقات ملموسة لـ CharsetEncoder وCharsetDecoder. تذكر أن مجموعة الأحرف عبارة عن مجموعة من الأحرف المشفرة وأنظمة التشفير/فك التشفير. كما رأينا من قبل، فإن التشفير وفك التشفير يكونان متماثلين تقريبًا على مستوى واجهة برمجة التطبيقات (API). يتم هنا مناقشة موجزة لما هو مطلوب لتنفيذ برنامج التشفير: وينطبق الشيء نفسه على بناء وحدة فك التشفير.
كما هو الحال مع Charset، لا يحتوي CharsetEncoder على مُنشئ افتراضي، لذلك تحتاج إلى استدعاء super() في مُنشئ الفئة الملموسة، مع توفير المعلمات المطلوبة.
من أجل توفير تطبيق CharsetEncoder الخاص بك، يجب عليك على الأقل توفير طريقة encodeLoop () الملموسة. بالنسبة لخوارزميات التشفير البسيطة، يجب أن يعمل التنفيذ الافتراضي للطرق الأخرى بشكل جيد. لاحظ أن encodeLoop() يأخذ معلمات مشابهة لتلك الخاصة بـ encode()، باستثناء العلامة المنطقية. تمثل طريقة encode () التشفير الفعلي لـ encodeLoop()، والتي تحتاج فقط إلى الانتباه إلى الأحرف المستهلكة من معلمة CharBuffer، وإخراج البايتات المشفرة إلى ByteBuffer المتوفرة.
الآن بعد أن رأينا كيفية تنفيذ مجموعات الأحرف المخصصة، بما في ذلك برامج التشفير وأجهزة فك التشفير المرتبطة، فلننظر إلى كيفية توصيلها بـ JVM حتى نتمكن من تشغيل التعليمات البرمجية باستخدامها.
قم بتوفير مجموعة الأحرف المخصصة الخاصة بك:
من أجل توفير تطبيق Charset الخاص بك لبيئة تشغيل JVM، يجب عليك إنشاء فئات فرعية محددة من فئة CharsetProvider في java.nio.charsets.-spi، كل منها بمنشئ بدون معلمات. يعد المنشئ بدون معلمات مهمًا لأنه سيتم تحديد موقع فئة CharsetProvider الخاصة بك عن طريق قراءة الاسم المؤهل بالكامل لملف التكوين. سيتم بعد ذلك استيراد سلسلة اسم الفئة هذه إلى Class.newInstance() لإنشاء مثيل للموفر الخاص بك، والذي يعمل فقط من خلال المُنشئ بدون معلمات.
يحدد ملف التكوين الذي يقرأه JVM موقع موفر مجموعة الأحرف، والذي يسمى java.nio.charset.spi.CharsetProvider. وهو موجود في الدليل المصدر (META-INF/الخدمات) في مسار فئة JVM. يحتوي كل JavaArchive (JAR) على دليل META-INF الذي يحتوي على معلومات حول الفئات والموارد الموجودة في JAR هذا. يمكن أيضًا وضع دليل يسمى META-INF أعلى الدلائل العادية في مسار فئة JVM.
واجهة برمجة تطبيقات CharsetProvider عديمة الفائدة تقريبًا. يحدث العمل الفعلي لتوفير مجموعة أحرف مخصصة في إنشاء فئات Charset وCharsetEncoder وCharsetDecoder المخصصة. يعد CharsetProvider مجرد وسيط بين مجموعة الأحرف الخاصة بك وبيئة التشغيل.
يوضح المثال 6-3 تنفيذ Charset وCharsetProvider المخصصين، بما في ذلك رمز العينات الذي يوضح استخدام مجموعة الأحرف والتشفير وفك التشفير وCharset SPI. المثال 6-3 يطبق مجموعة أحرف مخصصة.
مثال 6-3. مجموعة أحرف Rot13 مخصصة
الحزمة com.ronsoft.books.nio.charset؛
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.util.Map;
import java.util.Iterator;
استيراد java.io.Writer؛
استيراد java.io.PrintStream؛
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.io.BufferedReader;
استيراد java.io.InputStreamReader؛
استيراد java.io.FileReader؛
/**
* تطبيق Charset الذي ينفذ تشفير Rot13 هو
* خوارزمية بسيطة لتشويش النص تقوم بتغيير الحروف الأبجدية بمقدار 13
* بحيث يتحول "a" إلى "n"، و"o" إلى "b"، وما إلى ذلك. وقد انتشرت هذه الخوارزمية
* بواسطة منتديات المناقشة Usenet منذ سنوات عديدة لإخفاء الكلمات البذيئة وإخفاءها
* الإجابات على الأسئلة، وما إلى ذلك، خوارزمية Rot13 متماثلة ومطبقة
* إلى النص الذي تم خلطه بواسطة Rot13 سيعطيك النص الأصلي
* نص غير مرتب.
*
* تطبيق تشفير Charset هذا على دفق الإخراج سيؤدي إلى كل شيء
* اكتب إلى هذا الدفق ليتم تشفيره على Rot13 كما هو مكتوب ومطبق
* يؤدي نقله إلى دفق الإدخال إلى إلغاء تشفير البيانات المقروءة بواسطة Rot13 أثناء قراءتها.
*
* @ المؤلف رون هيتشنز ([email protected])
*/
الطبقة العامة Rot13Charset تمتد مجموعة الأحرف {
// اسم ترميز مجموعة الأحرف الأساسية الذي نفوضه
السلسلة النهائية الثابتة الخاصة BASE_CHARSET_NAME = "UTF-8"؛
// التعامل مع مجموعة الأحرف الحقيقية التي سنستخدمها لتحويل الترميز بين
// الأحرف والبايتات، يتيح لنا القيام بذلك تطبيق Rot13
// خوارزمية لترميزات مجموعة الأحرف متعددة البايت ولكن فقط
// سيتم تدوير أحرف ألفا ASCII، بغض النظر عن الترميز الأساسي.
Charset baseCharset;
/**
* منشئ لمجموعة أحرف Rot13 اتصل بمنشئ الفئة الفائقة
* قم بتمرير الاسم (الأسماء) الذي سنعرف به ثم احفظ مرجعًا إلى
* مندوب مجموعة الأحرف.
*/
محمية Rot13Charset(String canonical, String[] aliases) {
super(canonical, aliases);
// احفظ مجموعة الأحرف الأساسية التي نفوضها
baseCharset = Charset.forName(BASE_CHARSET_NAME);
}
//---------------------------------------------------------------- ----------
/**
* يتم استدعاؤها من قبل مستخدمي مجموعة الأحرف هذه للحصول على برنامج التشفير هذا
* إنشاء مثيل لفئة خاصة (المحددة أدناه) وتمريره
* برنامج تشفير من مجموعة الأحرف الأساسية.
*/
عامة CharsetEncoder newEncoder() {
إرجاع Rot13Encoder الجديد (this، baseCharset.newEncoder())؛
}
/**
* تم استدعاؤه من قبل مستخدمي مجموعة الأحرف هذه للحصول على وحدة فك ترميز هذا التنفيذ
* إنشاء مثيل لفئة خاصة (المحددة أدناه) وتمريره
* وحدة فك ترميز من مجموعة الأحرف الأساسية.
*/
عامة CharsetDecoder newDecoder() {
إرجاع Rot13Decoder الجديد (this، baseCharset.newDecoder())؛
}
/**
* يجب تنفيذ هذه الطريقة بواسطة Charsets ملموسة ونحن دائما نقول لا،.
* وهو آمن.
*/
منطقية عامة تحتوي على (Charset cs) {
عودة (خطأ) ؛
}
/**
* روتين مشترك لتدوير جميع أحرف ألفا ASCII في المعطى
* CharBuffer بواسطة 13. لاحظ أن هذا الرمز يقارن بوضوح بين العلوي و
* أحرف ASCII صغيرة الحجم بدلاً من استخدام الطرق
* Character.isLowerCase و Character.isUpperCase وذلك لأن
* نظام التدوير بمقدار 13 يعمل بشكل صحيح فقط مع الأحرف الأبجدية
* يمكن لمجموعة أحرف ASCII وتلك الأساليب أن تعود صحيحة بالنسبة إلى Unicode غير ASCII
* أحرف.
*/
تعفن الفراغ الخاص 13 (CharBuffer cb) {
لـ (int pos = cb.position(); pos < cb.limit(); pos++) {
شار ج = cb.get(pos);
شار أ = '/u0000';
// هل هو حرف ألفا صغير؟
إذا ((ج >= 'أ') && (ج <= 'ض')) {
أ = 'أ'؛
}
// هل هو حرف ألفا كبير؟
إذا ((ج >= 'أ') && (ج <= 'Z')) {
أ = 'أ'؛
}
// إذا كان الأمر كذلك، فقم بتدويره بمقدار 13
إذا (أ!= '/u0000') {
ج = (شار) ((((ج - أ) + 13) % 26) + أ)؛
cb.put(pos, c);
}
}
}
//---------------------------------------------------------------- --------
/**
* تطبيق التشفير لـ Rot13 Chars et
* يجب أيضًا أن تتجاوز فئة وحدة فك التشفير المطابقة أدناه الأساليب "impl"،
* مثل implOnMalformedInput() وإجراء مكالمات عبور إلى ملف
* تم ترك كائن baseEncoder كتمرين للمتسلل.
*/
فئة خاصة Rot13Encoder تمتد CharsetEncoder {
خاص CharsetEncoder baseEncoder؛
/**
* منشئ، استدعاء منشئ الطبقة الفائقة مع كائن Charset
* وأحجام الترميزات من جهاز التشفير المندوب.
*/
Rot13Encoder(Charset cs, CharsetEncoder baseEncoder) {
super(cs, baseEncoder.averageBytesPerChar(), baseEncoder
.maxBytesPerChar());
this.baseEncoder = baseEncoder;
}
/**
* تنفيذ حلقة التشفير أولا، نقوم بتطبيق Rot13
* تحويل الخوارزمية إلى CharBuffer، ثم إعادة ضبط جهاز التشفير
* مجموعة الأحرف الأساسية واستدعاء طريقة التشفير () للقيام بالأمر الفعلي
* الترميز قد لا يعمل بشكل صحيح مع مجموعات الأحرف غير اللاتينية
* يمكن قراءة CharBuffer الذي تم تمريره فقط أو إعادة استخدامه من قبل المتصل
* أغراض أخرى لذلك نقوم بتكرارها وتطبيق تشفير Rot13 على ملف
* نسخ نحن نريد التقدم إلى موضع المخزن المؤقت للإدخال
* تعكس الأحرف المستهلكة.
*/
محمية CoderResult encodeLoop(CharBuffer cb, ByteBuffer bb) {
CharBuffer tmpcb = CharBuffer.allocate(cb.remaining());
بينما (cb.hasRemaining()) {
tmpcb.put(cb.get());
}
tmpcb.rewind();
rot13(tmpcb);
baseEncoder.reset();
CoderResult cr = baseEncoder.encode(tmpcb, bb, true);
// إذا حدث خطأ أو تجاوز حجم الإخراج، فنحن بحاجة إلى التعديل
// موضع المخزن المؤقت للإدخال ليطابق ما
// تم استهلاكه بالفعل من المخزن المؤقت المؤقت
// التدفق السفلي (يتم استهلاك جميع المدخلات)، هذا أمر محظور.
cb.position(cb.position() - tmpcb.remaining());
العودة (كر)؛
}
}
//---------------------------------------------------------------- --------
/**
* تنفيذ وحدة فك التشفير لمجموعة Rot13 Charset.
*/
فئة خاصة Rot13Decoder تمتد CharsetDecoder {
خاص CharsetDecoder baseDecoder؛
/**
* منشئ، استدعاء منشئ الطبقة الفائقة مع كائن Charset
* وتمرير قيم الأحرف/البايت من وحدة فك ترميز المفوض.
*/
Rot13Decoder(Charset cs, CharsetDecoder baseDecoder) {
super(cs, baseDecoder.averageCharsPerByte(), baseDecoder
.maxCharsPerByte());
this.baseDecoder = baseDecoder;
}
/**
* تنفيذ حلقة فك التشفير أولاً، نقوم بإعادة ضبط وحدة فك التشفير
* مجموعة الأحرف الأساسية، ثم اتصل بها لفك تشفير البايتات إلى أحرف،
* يتم بعد ذلك إلغاء تشفير رمز النتيجة باستخدام ملف CharBuffer
* يتم إرجاع خوارزمية Rot13 ورمز النتيجة
* بشكل صحيح لمجموعات الأحرف غير اللاتينية.
*/
محمية CoderResult decodeLoop(ByteBuffer bb, CharBuffer cb) {
baseDecoder.reset();
نتيجة CODERRESULT = referedecoder.decode (BB ، CB ، TRUE) ؛
rot13 (CB) ؛
العودة (نتيجة) ؛
}
}
// ----------------------------------------------- --------
/**
* اختبار الوحدة لـ ROT13 Charset.
* ملف إذا تم تسميته على سطر الأوامر ، أو stdin إذا لم يتم توفير أي args ، و
* اكتب المحتويات إلى stdout عبر تشفير X -rot13
* "التشفير" التي تنفذها خوارزمية ROT13 متناظرة
* في ملف نص عادي ، مثل رمز مصدر Java على سبيل المثال ، سوف يخرج أ
* نسخة مخفوقة.
* مستند نص عادي أصلي.
*/
يرمي الفراغ الثابت العام (سلسلة [] argv) الاستثناء {
BufferedReader في ؛
if (argv. length> 0) {
// افتح الملف المسماة
في = جديد BufferedReader (New FileReader (Argv [0])) ؛
} آخر {
// لف زميل BufferedReader حول stdin
في = جديد bufferedReader (inputStreamReader جديد (system.in)) ؛
}
// قم بإنشاء printstream يستخدم ترميز ROT13
printstream out = new printstream (system.out ، false ، "x -rot13") ؛
سلسلة ق = فارغة؛
// اقرأ كل الإدخال واكتبها إلى الإخراج.
// مع مرور البيانات عبر printstream ،
// سيتم ترميز rot13.
بينما ((s = in.readline ())! = null) {
out.println (s) ؛
}
out.flush();
}
}
مثال 6-4.
حزمة com.ronsoft.books.nio.charset ؛
استيراد java.nio.charset.charset ؛
استيراد java.nio.charset.spi.charsetProvider ؛
import java.util.HashSet;
استيراد java.util.iterator ؛
/**
* فئة charsetprovider التي توفر المتاحة
* Ronsoft
* ليس Iana charset مسجل ، لذلك يبدأ الاسم بـ "X-" لتجنب الاسم
* الاشتباكات مع charsets الخارجة.
*
* لتفعيل هذا charsetprovider ، من الضروري إضافة ملف إلى
* classpath من وقت تشغيل JVM في الموقع التالي:
* meta-inf/services/java.nio.charsets.spi.charsetProvider
*
* يجب أن يحتوي هذا الملف على سطر مع الاسم المؤهل تمامًا لهذا الفئة على
* سطر في حد ذاته: com.ronsoft.books.nio.charset.ronsoftcharsetprovider java
* NIO 216
*
* انظر صفحة Javadoc لـ java.nio.charsets.spi.charsetProvider
* تفاصيل.
*
* Author Ron Hitchens ([email protected])
*/
الطبقة العامة RonsoftcharsetProvider يمتد CharsetProvider {
// اسم Charset الذي نقدمه
Static Final Final String charset_name = "X-Rot13" ؛
// مقبض لكائن charset
charset private rot13 = null ؛
/**
* مُنشئ ، إنشاء كائن charset وحفظ المرجع.
*/
Public RonsoftcharsetProvider () {
this.rot13 = new Rot13Charset (charset_name ، سلسلة جديدة [0]) ؛
}
/**
* دعا من قبل charset الأساليب لإيجاد charset اسمه
* إنه اسم هذا الشارست (ليس لدينا أي أسماء مستعارة) ثم إعادة
* rot13 charset ، مرة أخرى عودة فارغة.
*/
charset charsetforname (String charsetName) {
if (charsetname.equalsignorecase (charset_name)) {
العودة (rot13) ؛
}
العودة (فارغة) ؛
}
/**
* إرجاع جهاز التكرار على مجموعة كائنات Charset التي نقدمها.
*
* @إعادة كائن Iterator الذي يحتوي
* الكائنات التي توفرها هذه الفئة.
*/
ITERATOR العام <HARSET> charsets () {
Hashset <Charset> set = new hashset <charset> (1) ؛
set.add (rot13) ؛
return (set.iterator ()) ؛
}
}
إضافة X -ROT13 إلى قائمة مجموعة الأحرف في المثال 6-1 ينتج هذا الإخراج الإضافي:
Charset: X-Rot13
المدخلات: żmaana؟
مشفرة:
0: C2 (ż)
1: BF (ż)
2: 5A (Z)
3: 6e (ن)
4: C3 (ă)
5: B1 (±)
6: 6e (ن)
7:61 (أ)
8: 6e (ن)
9: 3F (؟)
Charset (فئة مجموعة الأحرف)
مخطط ترميز مجموعة الأحرف الذي يتضمن الترميز المستخدم لتمثيل تسلسل الأحرف الذي يختلف عن الأحرف التي يتم تعيينها كتسلسل للبايت.
charsetencoder (فئة تشفير مجموعة الأحرف)
تشفير المحرك يحول تسلسل الأحرف إلى تسلسل بايت. يمكن بعد ذلك فك ترميز تسلسل البايت لإعادة بناء تسلسل حرف المصدر.
CharsetDecoder (فئة فك ترميز Charset)
يحول محرك فك التشفير تسلسل البايت المشفر إلى تسلسل حرف.
CharsetProvider SPI (مزود Charset SPI)
حدد موقع تطبيق charset وجعله متاحًا من خلال آلية بائع الخادم للاستخدام في بيئة وقت التشغيل.