JavaRush /مدونة جافا /Random-AR /فئة السلسلة
articles
مستوى

فئة السلسلة

نشرت في المجموعة
  • طُرق
  • توليد سلسلة عشوائية
  • مقارنة السلسلة: equals()أو ==؟
غالبًا ما يستخدم المبرمجون هذا الفصل String، لذا يجب تعلمه جيدًا. سلسلة الفئة - 1تذكر أن كائنات الفئة Stringغير قابلة للتغيير. لذلك عندما تعتقد أنك تقوم بتغيير سلسلة، فإنك في الواقع تقوم بإنشاء سلسلة جديدة. تحتوي Java على فئات خاصة StringBufferتسمح StringBuilderبإجراء تغييرات على السلسلة. تم تعريف الفئات String، في الحزمة java.lang وهي متاحة تلقائيًا بدون إعلان استيراد StringBuffer. جميع الفئات الثلاث تنفذ الواجهة . إنشاء سلسلة أمر بسيط للغاية. على سبيل المثال، يمكنك القيام بذلك: StringBuilderCharSequence
String aboutCat = "Кот - это звучит гордо, а если наступить на хвост, то громко";
يمكنك إنشاء مجموعة من السلاسل:
String[] cats = {"Васька", "Barsik", "Murzik"};
يمكنك إنشاء كائن فئة فارغ String:
String str = new String();
يمكنك إنشاء سلسلة عبر مصفوفة أحرف:
char[] chars = { 'c', 'a', 't' };
String str = new String(chars);
يوجد أيضًا مُنشئ يسمح لك بتعيين نطاق مصفوفة الأحرف. تحتاج إلى تحديد بداية النطاق وعدد الأحرف المراد استخدامها:
char[] chars = {'c', 'a', 't', 'a', 'm', 'a', 'r', 'a', 'n' };
String str = new String(chars, 0, 3);
يمكنك إنشاء كائن فئة Stringمن كائن فئات StringBufferباستخدام StringBuilderالمنشئات التالية:
String(StringBuffer an object_StrBuf)
String(StringBuilder an object_StrBuild)

عوامل التشغيل +و+= لـString

في Java، علامة الزائد ( +) تعني تسلسل السلاسل، وبعبارة أخرى، دمج السلاسل.
String cat = "Кот";
String name = "Васька";
//складываем две строки и пробел между ними, чтобы слова не слиплись
String fullname = cat + " " + name; // получится Кот Васька
إذا كان أحد المعاملات في التعبير يحتوي على سلسلة، فيجب أن تكون المعاملات الأخرى عبارة عن سلاسل أيضًا. لذلك، يمكن لـ Java نفسها إرسال المتغيرات إلى تمثيل سلسلة، حتى لو لم تكن سلاسل.
int digit = 4;
String paws = " лапы";
String aboutcat = digit + paws; // хотя мы складываем число и строку, но все равно получим строку
За кулисами Java за нас преобразовало число 4 в строку "4"

تنسيق السلاسل

لنفترض أن لدينا مورد سلسلة:
<string name="aboutcat">У кота по имени Барсик четыре лапы, один хвост. Ему 5 лет</string>
لعرض هذه السلسلة برمجياً في العنصر TextView، يمكنك استخدام الكود:
TextView tvCatsInfo = (TextView)findViewById(R.id.textView1);
tvCatsInfo.setText(R.string.aboutcat);
تخيل أن لديك عدة قطط. يمكنك، بالطبع، أن يكون لديك خط خاص بك لكل قطة. لكن الخطوط متشابهة جدًا، فقط الأسماء والأعمار تتغير. يمكنك أيضًا تغيير عدد الكفوف والذيول (ما الذي تدخنه؟). في مثل هذه الحالات، يمكنك تطبيق تنسيق السلسلة. نحتاج إلى تحديد الكلمات التي سنغيرها واستبدالها بمجموعة خاصة من الأحرف التي تبدأ برمز النسبة المئوية، ثم رقم متزايد بواحد، ثم $sللسلاسل أو $dللأرقام. لذلك دعونا نغير مورد السلسلة الخاص بنا مثل هذا:
<string name="aboutcat">У кота по имени %1$s %2$s лапы, %3$s хвост. Ему %4$d лет</string>
دعونا نجري تغييرات على الكود:
String strBarsik = "Barsik";
String strPaws = "четыре";
String strTail = "one";
int year = 5;
String strCats = getResources().getString(R.string.aboutcat);
String strFinal = String.format(strCats, strBarsik, strPaws, strTail, year);
tvCatsInfo.setText(strFinal);
إذا كان لديك قطة فاسكا وعمرها ست سنوات، فقم بإضافة متغيرين وقم بتنسيق الخط
String strVaska = "Васька";
year = 6;
String strFinal = String.format(strCats, strVaska, strPaws, strTail, year);
tvCatsInfo.setText(strFinal);
فيما يلي مثال بسيط على التنسيق. تذكرها واستخدمها في الأماكن الصحيحة.

مورد السلسلة

يُنصح بتخزين السلاسل في الموارد. يتم الوصول برمجيًا إلى مورد السلسلة على النحو التالي:
String catName = getResources().getString(R.string.barsik);

استخراج السلاسل من صفائف السلسلة في الموارد

لنفترض أن لديك مصفوفة سلسلة محددة في ملف strings.xml المسمى cats_array. ثم يمكنك الوصول إلى الصفوف من الموارد مثل هذا:
Resources res = getResources();
String[] cats = res.getStringArray(R.array.cats_array);

طُرق

public char charAt (int index) إرجاع الحرف عند الإزاحة المحددة في هذه السلسلة. العد التنازلي يبدأ من 0. لا داعي لاستخدام قيم سلبية وغير موجودة، كن أكثر جدية. لاستخراج أحرف متعددة استخدم getChars().
String testString = "Котёнок";
char myChar = testString.charAt(2);
tv.setText(Character.toString(myChar)); // выводит третий символ - т
public int codePointAt(int index) إرجاع حرف Unicode في الفهرس المحدد
String testString = "Котёнок";
int myChar = testString.codePointAt(3);
tv.setText(String.valueOf(myChar)); // возвращает 1105
public int codePointBefore(int index) إرجاع حرف Unicode الذي يسبق الفهرس المحدد
String testString = "Котёнок";
int myChar = testString.codePointBefore(4);
tv.setText(String.valueOf(myChar)); // возвращает 1105
public int codePointCount(int start, int end) حساب عدد أحرف Unicode بين المواضع startوend
String testString = "Котёнок";
int myChar = testString.codePointCount(0, 3);
tv.setText(String.valueOf(myChar)); // возвращает 3
public int compareTo(String string) يقارن السلسلة المحددة باستخدام قيم أحرف Unicode ويحسب السلسلة التي تكون أقل من أو تساوي أو أكبر من السلسلة التالية. يمكن استخدامها للفرز. تؤخذ الحالة بعين الاعتبار. إذا تطابقت السلاسل، فسيتم إرجاع 0، وإذا كانت أقل من الصفر، فستكون سلسلة الاستدعاء أقل من السلسلة string، وإذا كانت أكبر من الصفر، فستكون سلسلة الاستدعاء أكبر من السلسلة string. يتم وضع الكلمات ذات الأحرف الكبيرة فوق الكلمات ذات الأحرف الصغيرة.
String testString = "Котёнок";

if (testString.compareTo("котёнок") == 0) {
    tvInfo.setText("Строки равны");
} else {
    tvInfo.setText("Строки не равны. Возвращено"
            + testString.compareTo("котёнок")); // возвращает -32
}
لنقم بفرز مجموعة السلاسل باستخدام الفرز الفقاعي.
String[] poem = { "Мы", "везём", "с", "собой", "кота" };

for (int j = 0; j < poem.length; j++) {
	for (int i = j + 1; i < poem.length; i++) {
		if (poem[i].compareTo(poem[j]) < 0) {
			String temp = poem[j];
			poem[j] = poem[i];
			poem[i] = temp;
		}
	}
	System.out.println(poem[j]);
}
ونتيجة لذلك نحصل على:
Мы
везём
кота
с
собой
كما ترى، فإن تغيير أماكن المصطلحات لا يغير مجموع فرز القطط. public int compareToIgnoreCase (String string) يقارن السلسلة المحددة باستخدام قيم أحرف Unicode، غير حساسة لحالة الأحرف.
String testString = "Котёнок";

if (testString.compareToIgnoreCase("котёнок") == 0) {
    tv.setText("Строки равны"); // слова одинаковы, если не учитывать регистр
} else {
    tv.setText("Строки не равны. Возвращено"
            + testString.compareTo("котёнок"));
}
public String concat (String string) يسلسل سلسلة مع السلسلة المحددة. يتم إرجاع سلسلة جديدة تحتوي على سلسلة السلسلتين. لاحظ أن اسم الطريقة نفسه يحتوي على قطة!
String testString = "Сук";
String newString = testString.concat("кот");
tv.setText(newString);
تؤدي الطريقة نفس وظيفة العامل +ويمكن كتابتها Сук + кот. لكن الشخص الحقيقي للقطط سيستخدم طريقة "القط". public boolean contains (CharSequence cs) تحديد ما إذا كانت السلسلة تحتوي على سلسلة من الأحرفCharSequence
String testString = "котёнок";

if(testString.contains("кот")){
    infoTextView.setText("В слове котёнок содержится слово кот!");
}
public static String copyValueOf (char[] data, int start, int length) ينشئ سلسلة جديدة تحتوي على الأحرف المحددة من المصفوفة Dataبدءًا من الموضع start(الترقيم الصفري) للطول length . public static String copyValueOf(char[] data) إنشاء سلسلة جديدة تحتوي على أحرف من المصفوفة المحددة. لا يؤدي تغيير المصفوفة بعد إنشاء الصف إلى تغيير الصف الذي تم إنشاؤه. public boolean endsWith(String suffix) للتحقق مما إذا كانت السلسلة تنتهي بـ suffix.
String str1 = "Суккот";

if(str1.endsWith("кот"))
    infoTextView.setText("Слово заканчивается на котике");
else
    infoTextView.setText("Плохое слово. Нет смысла его использовать");
public boolean equals (Object string) يقارن الكائن المحدد والسلسلة ويعيد القيمة الصحيحة إذا كانت السلاسل المقارنة متساوية، على سبيل المثال. يحتوي على نفس الأحرف وبنفس الترتيب الحساس لحالة الأحرف.
String str1 = "Кот";
String str2 = "Кошка";

if(str1.equals(str2))
    infoTextView.setText("Строки совпадают");
else
    infoTextView.setText("Строки не совпадают");
لا ينبغي الخلط بين هذه الطريقة والعامل ==الذي يقارن بين مرجعين للكائنات ويحدد ما إذا كانا يشيران إلى نفس المثيل. انظر مقارنة السلسلة: equals()أو ==؟ public boolean equalsIgnoreCase(String string) يقارن السلسلة المحددة بالسلسلة المصدر بطريقة غير حساسة لحالة الأحرف ويعيد القيمة true إذا كانتا متساويتين. يعتبر النطاق من الألف إلى الياء مساويا للنطاق من الألف إلى الياء.
String str1 = "Кот";
String str2 = "кот";

if(str1.equalsIgnoreCase(str2))
    infoTextView.setText("Строки совпадают");
else
    infoTextView.setText("Строки не совпадают");
public static String format(Locale locale, String format, Object... args)
تقوم بإرجاع سلسلة منسقة باستخدام التنسيق المقدم والوسائط المترجمة إلى النطاق المحدد. على سبيل المثال التاريخ أو الوقت
// выводим число типа float с двумя знаками после запятой
String.format("%.2f", floatValue);
نلصق كلمتين تظهران في سطر جديد. في هذه الحالة، يتم عرض الكلمة الثانية بأحرف كبيرة.
String str1 = "Кот";
String str2 = "васька";
String strResult = String.format("%s\n%S", str1, str2);
// выводим результат в TextView
infoTextView.setText(strResult);
تحويل الرقم إلى النظام الثماني.
String str1 = "8";
int inInt = Integer.parseInt(str1); // конвертируем строку в число
String strResult = String.format("(Восьмеричное meaning): %o\n", inInt);

infoTextView.setText(strResult);
وقياسا على ذلك، فإننا نعرض في النظام الست عشري
String str1 = "255";
int inInt = Integer.parseInt(str1);
String strResult = String.format("(Шестнадцатеричное meaning): %x\n", inInt);
// число 255 будет выведено How ff
infoTextView.setText(strResult);
للاستخدام بالأحرف الكبيرة %X، ffسيكون FF. لاستخدام النظام العشري %d. يمكن أيضًا عرض التاريخ بطرق مختلفة.
Date now = new Date();
Locale locale = Locale.getDefault();
infoTextView.setText(
		String.format(locale, "%tD\n", now) + // (MM/DD/YY)
		String.format(locale, "%tF\n", now) + // (YYYY-MM-DD)
		String.format(locale, "%tr\n", now) + // Full 12-hour time
		String.format(locale, "%tz\n", now) + // Time zone GMT offset
		String.format(locale, "%tZ\n", now)); // Localized time zone bbreviation
public byte[] getBytes(String charsetName) إرجاع سلسلة منسقة باستخدام التنسيق المقدم. public void getBytes(int start, int end, byte[] data, int index)والأحمال الزائدة الأخرى تقوم الطريقة بتخزين الأحرف في مصفوفة بايت، وهي بديل لـ getChars(). يُستخدم غالبًا عند تصدير سلاسل من مصادر مختلفة تستخدم أحرف Unicode أخرى. على سبيل المثال، تعمل Java بشكل افتراضي مع أحرف Unicode ذات 16 بت، وعلى الإنترنت، تستخدم السلاسل غالبًا Unicode 8 بت وASCII وما إلى ذلك. public void getChars(int start, int end, char[] buffer, int index) طريقة لاستخراج أحرف متعددة من سلسلة. تحتاج إلى تحديد فهرس بداية السلسلة الفرعية ( start)، وفهرس الحرف الذي يلي نهاية السلسلة الفرعية المراد استخراجها (النهاية). المصفوفة التي تستقبل الأحرف المحددة موجودة في المعلمة المخزن المؤقت . يتم تمرير الفهرس الموجود في المصفوفة، والذي سيتم كتابة السلسلة الفرعية منه، في معلمة الفهرس . تأكد من أن المصفوفة كبيرة بما يكفي لتحتوي على كافة الأحرف الموجودة في السلسلة الفرعية المحددة.
String unusualCat = "Котёнок по имени Гав";
int start = 5;
int end = 12;
char[] buf = new char[end - start];
unusualCat.getChars(start, end, buf, 0);
infoTextView.setText(new String(buf));
public int hashCode() تقوم بإرجاع عدد صحيح يمثل رمز التجزئة لهذا الكائن. public int indexOf(int c) إرجاع رقم الموضع الأول الذي تمت مواجهته مع الفهرس المحدد c.
String testString = "котёнок";
// символ ё встречается в четвёртой позиции (index = 3)
infoTextView.setText(String.valueOf(testString.indexOf("ё")));
public int indexOf (int c, int start) يبحث عن الفهرس من، بدءًا من الموضعstart
String testString = "котёнок";
// вернёт -1, так How после 5 символа буквы ё нет
infoTextView.setText(String.valueOf(testString.indexOf("ё", 4)));
public int indexOf (String string) يبحث عن سلسلة من الأحرفsubString
String testString = "У окошка";
infoTextView.setText(String.valueOf(testString.indexOf("кошка")));
public int indexOf (String subString, int start) يبحث عن سلسلة من الأحرف subStringتبدأ من الموضعstart
String testString = "У окошка";
infoTextView.setText(String.valueOf(testString.indexOf("кошка", 2)));
public String intern () "تجزئات" سلسلة public boolean isEmpty () تتحقق مما إذا كانت السلسلة فارغة
if(catname.isEmpty()) {
    // здесь ваш code
}
ظهرت هذه الطريقة في API 9 (Android 2.1). بالنسبة للأجهزة القديمة، استخدم String.length() == 0 public int lastIndexOf (String string) и другие перегруженные версии إرجاع رقم آخر موضع تمت مواجهته في الفهرس المحدد. على سبيل المثال، يمكنك الحصول على اسم الملف بدون ملحق مثل هذا:
filename.toString().substring(0, filename.getString().lastIndexOf("."));
في هذا المثال، نحصل على موضع النقطة الأخيرة ونحصل على السلسلة الفرعية قبلها. public int length() إرجاع طول السلسلة
String testString = "котёнок";
infoTextView.setText(String.valueOf(testString.length())); // возвращает 7 (семь символов)
public boolean matches(String regularExpression) يتحقق مما إذا كانت السلسلة تتطابق مع التعبيرات العادية.
public int offsetByCodePoints (int index, int codePointOffset)
تُرجع موضعًا يقع على مسافة codePointOffsetبعد موضع البداية المحدد بواسطة المعلمة، index public boolean regionMatches (int thisStart, String string, int start, int length) وتقارن الطريقة الجزء المحدد من السلسلة بجزء آخر من السلسلة. يجب عليك تحديد فهرس بداية نطاق الصف للكائن المتصل بالفئة String. يتم تمرير السلسلة المراد مقارنتها في المعلمة string. يتم تمرير فهرس الحرف الذي يجب إجراء المقارنة منه في المعلمة start، ويتم تمرير طول السلسلة الفرعية المراد مقارنتها في المعلمة length. public boolean regionMatches (boolean ignoreCase, int thisStart, String string, int start, int length) نسخة مثقلة. تقارن الطريقة الجزء المحدد من السلسلة بجزء آخر من السلسلة، متجاهلة حالة الأحرف. public String replace(CharSequence target, CharSequence replacement) и другие перегруженные версии تغيير حرف أو تسلسل من الأحرف targetإلىreplacement
String testString = "whale";
// меняем и на о
infoTextView.setText(testString.replace("And", "о")); // возвращается кот
public String replaceAll (String regularExpression, String replacement) public String replaceFirst (String regularExpression, String replacement) يزيل الأحرف الأولى باستخدام التعبير العادي. على سبيل المثال، إذا كنت بحاجة إلى إزالة الأصفار في بداية الأرقام 001، 007، 000024، فيمكنك استخدام هذه المكالمة.
String s = "001234-cat";
String s = s.replaceFirst ("^0*", ""); // останется 1234-cat
public String[] split (String regularExpression) и другие перегруженные версии يقسم سلسلة إلى مجموعة من الكلمات. على سبيل المثال، هناك سلسلة Vaska Ryzhik Murzik Barsik ونريد الحصول على مجموعة من أسماء القطط:
String catnames = "Васька Рыжик Мурзик Барсик";
String aCats[] = catnames.split(" ");  // по пробелу
نحن نحصل:
aCats[0] = Васька
aCats[1] = Рыжик
aCats[2] = Мурзик
aCats[3] = Барсик
public boolean startsWith(String prefix) يتحقق مما إذا كانت السلسلة تبدأ بأحرف prefixمن بداية السلسلة
String str1 = "котлета";

if(str1.startsWith("кот"))
    infoTextView.setText("Слово содержит кота");
else
    infoTextView.setText("Плохое слово. Нет смысла его использовать");
public boolean startsWith(String prefix, int start) للتحقق مما إذا كانت السلسلة المعطاة تبدأ بأحرف prefixفي الموضع المحدد.
String str1 = "Суккот";

if(str1.startsWith("кот", 3))
    infoTextView.setText("Слово содержит кота");
else
    infoTextView.setText("Плохое слово. Нет смысла его использовать");
public CharSequence subSequence (int start, int end) مشابهة للطريقة substring()، ولكن يمكن استخدامها ل CharSequence. public String substring(int start)والأحمال الزائدة الأخرى إنشاء تسلسل/سلسلة جديدة بأحرف من السلسلة المحددة تبدأ من الموضع startإلى نهاية السطر/تنتهي بالحرف في الموضع end. يحتوي السطر الجديد على أحرف من startالنهاية - 1، لذلك نأخذ حرفًا آخر.
String testString = "скотина";

infoTextView.setText(testString.substring(1, 4)); // возвращается кот
public char[] toCharArray() نسخ الأحرف الموجودة في هذه السلسلة إلى مصفوفة أحرف. ويمكن الحصول على نفس النتيجة من خلال getChars(). لا توصي الوثائق باستخدام هذه الطريقة، وتقترح charAt().
String unusualCat = "Котёнок по имени Гав";

char[] yomoe = unusualCat.toCharArray();
infoTextView.setText(String.valueOf(yomoe[3]));
public String toLowerCase() и другие перегруженные версии تحويل سلسلة إلى حالة صغيرة. تتحكم اللغة الافتراضية في التحويل. سلسلة القط = "القط"؛ السلسلة السفلية = cat.toLowerCase(); infoTextView.setText(lower); public String toString () إرجاع سلسلة. بالنسبة للسلسلة نفسها، والتي هي في حد ذاتها سلسلة بالفعل، فإن إرجاع السلسلة لا معنى له (أوه، كيف ثنيتها). لكن هذه الطريقة مفيدة جدًا للفئات الأخرى. public String toUpperCase() تحويل سلسلة إلى حالة كبيرة. تتحكم اللغة الافتراضية في التحويل.
String cat = "Кот";
String upper = cat.toUpperCase();
infoTextView.setText(upper);
public String trim() إزالة المسافات في بداية ونهاية السلسلة.
String str = "   Hello Kitty  ".trim();
infoTextView.setText(str);
public static String valueOf(long value)والأحمال الزائدة الأخرى تحويل المحتويات (الأرقام والكائنات والأحرف ومصفوفات الأحرف) إلى سلسلة.
int catAge = 7; // это число

infoTextView.setText(String.valueOf(catAge)); // преобразовано в строку

توليد سلسلة عشوائية

لنفترض أننا بحاجة إلى سلسلة عشوائية من الأحرف المعطاة.
private static final String mCHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
private static final int STR_LENGTH = 9; // длина генерируемой строки
Random random = new Random();

public void onClick(View view) {
    TextView infoTextView = (TextView) findViewById(R.id.textViewInfo);
    infoTextView.setText(createRandomString());
}

public String createRandomString() {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < STR_LENGTH; i++) {
        int number = random.nextInt(mCHAR.length());
        char ch = mCHAR.charAt(number);
        builder.append(ch);
    }
    return builder.toString();
}

مقارنة السلسلة: equals()أو ==؟

لنلقي نظرة على مثال.
String str1 = "Murzik";
String str2 = new String(str1);
boolean isCat = str1 == str2;

infoTextView.setText(str1 + " == " + str2 + " -> " + isCat);
على الرغم من أن المتغيرين يحتويان على نفس الكلمة، إلا أننا نتعامل مع كائنين مختلفين وسيقوم عامل التشغيل ==بإرجاع false . ذات مرة، عندما كانت الأشجار كبيرة، كنت بحاجة إلى مقارنة سلسلتين من مصادر مختلفة. على الرغم من أن السلاسل تبدو متماثلة تمامًا، إلا أن المقارنة باستخدام عامل التشغيل ==أظهرت خطأً وأربكت جميع البطاقات بالنسبة لي. وعندها فقط اكتشفت أنني بحاجة إلى استخدام ملف equals(). السلسلة في Java هي كائن منفصل قد لا يكون مماثلاً لكائن آخر، على الرغم من أن مخرجات السلسلة قد تبدو نفسها على الشاشة. كل ما في الأمر هو أن Java، في حالة العامل المنطقي ==(وكذلك !=)، تقارن المراجع بالكائنات (لا توجد مشكلة من هذا القبيل عند العمل مع الأوليات):
String s1 = "hello";
String s2 = "hello";
String s3 = s1;
String s4 = "h" + "e" + "l" + "l" + "o";
String s5 = new String("hello");
String s6 = new String(new char[]{'h', 'e', 'l', 'l', 'o'});

infoTextView.setText(s1 + " == " + s2 + ": " + (s1 == s2));
// попробуйте и другие варианты
// infoTextView.setText(s1 + " equals " + s2 + ": " + (s1.equals(s2)));
// infoTextView.setText(s1 + " == " + s3 + ": " + (s1 == s3));
// infoTextView.setText(s1 + " equals " + s3 + ": " + (s1.equals(s3)));
// infoTextView.setText(s1 + " == " + s4 + ": " + (s1 == s4));
// infoTextView.setText(s1 + " equals " + s4 + ": " + (s1.equals(s4)));
// infoTextView.setText(s1 + " == " + s5 + ": " + (s1 == s5)); // false
// infoTextView.setText(s1 + " equals " + s5 + ": " + (s1.equals(s5)));
// infoTextView.setText(s1 + " == " + s6 + ": " + (s1 == s6)); // false
// infoTextView.setText(s1 + " equals " + s6 + ": " + (s1.equals(s6)));
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION