Учим Java. Работа со строками
Учим Java. Работа со строками В процессе программирования на Java слишком часто необходимо прибегать к изощренным методам обработки строк. В случае с Perl, например, проблема решалась использованием регулярных выражений. Java лишен встроенных средств для работы с регулярными выражениями, однако, тех возможностей, которые дает нам класс String, StringBuffer, StringTokenizer и прочие, более чем достаточно, чтобы решать проблемы обработки строк, или, как любят выражаться в учебниках, символьной информации.
Начнем с общеизвестного факта: любая символьная информация в Java представляется в Unicode. Это означает, что на каждый символ отводится не один, а два байта (16 бит). Любую последовательность символов в ASCII Java будет преобразовывать в Unicode. Однако сие не означает, что Java не работает с 8-битовыми последовательностями. Существует множество способов принудительного преобразования строк в байтовые массивы (byte[]), а также символов (char) в байты (byte) и наоборот.
Cоздать строку можно, например, вот так:
String strTmp = "Это строка в Unicode";
String strTmp = new String("Это строка в Unicode");
Оба этих способа аналогичны. Оба описывают переменную-ссылку типа String, которая указывает на только что созданный объект класса String. Первый варинт был принят для краткости.
Примеры, приведенные выше, создают строчки, одновременно их инициализируя, т.е. присваивая начальное значение "Это строка в Unicode". Помимо конструктора String(java.lang.String), также имеет место быть просто String(). Т.е. без задания начальной инициализации. Это иногда бывает полезно.
String strTmp = new String();
Важно понимать, что экземпляр класса String ни в коем случае не есть массив символов. Т.е. String и char[] — это абсолютно разные вещи и не стоит проводить аналогии с C. Поэтому к String не применим оператор [] для взятия символа с заданным индексом. Этим целям служит метод класса String charAt(int). Принимая целое значение индекса символа в строке, он возращает этот символ. Индексация строки начинается, естественно, с нуля.
char c = strTmp.charAt(0);
Если значение индекса превосходит значение длины строки, иными словами — выходит за границы длины строки, то выбрасывается исключение IndexOutOfBoundsException. Узнать длину строки позволяет метод length(). Обратите внимание, что это именно метод, в отличие от поля length в массивах, и его надо записывать с круглыми скобками.
Обратным целям служит серия методов для определения позиции символа в строке indexOf(). Методы возвращают индекс найденного вхождения символа или подстроки, либо -1 в случае неудачи.
int indexOf(char c) — первое вхождение символа ch, начиная с начала строки;
int indexOf(char c, int start) — первое вхождение символа ch, начиная с начала строки в позиции не меньше start;
int indexOf(String s) — первое вхождение подстроки s, начиная с начала строки;
int indexOf(String s, int start) — первое вхождение подстроки s, начиная с начала строки в позиции не меньше start;
int lastIndexOf(char c) — первое вхождение символа ch, начиная с конца строки;
int lastIndexOf(char c, int start) — первое вхождение символа ch, начиная с конца строки в позиции не больше start;
int lastIndexOf(String s) — первое вхождение подстроки s, начиная с конца строки;
int lastIndexOf(String s, int start) — первое вхождение подстроки s, начиная с конца строки в позиции не больше start.
Отдельной темы заслуживает конкатенация (склеивание) строк. В самом банальном варианте, чтобы добавить к одной строке другую, необходимо воспользоваться методом concat(java.lang.String), например:
strTmp.concat("подстрока 1").concat("подстрока 2").concat("подстрока 3");
Здесть к strTmp мы добавили три строчки. Аналогичную операцию можно было провести иначе:
strTmp += "подстрока 1" + "подстрока 2" + "подстрока 3";
Изначально в Java нет возможности для перегрузки операторов, в отличие от C++, например. Но операция склеивания строк встречается настолько часто, что разработчики решили облегчить труд программистов и сделать исключение. Они сами перегрузили опереторы += и + для объектов класса String, чем мы с удовольствием теперь пользуемся.
Для любой последовательности символов, заключенной в двойные кавычки, Java создает отдельный экземпляр класса String, поэтому не имеет значения, складываете ли вы константные строки, либо это переменные. Причем переменные могут быть разного типа, благодаря приведению "на лету".
Так как строки представленны в виде классов, было бы неправильно производить сравнение двух объектов на предмет совпадения значений строк обычным оператором ==. Т.к. в этом случае сравнивались бы объекты непосредственно. По этой причине для сравнения строк были введены методы equals() и equalsIgnoreCase(). Второй метод, в отличие от первого, игнорирует регистр символов:
String strTmp2 = "asdf";
if (!strTmp.equals(strTmp2)) System.out.println("Не совпадают!");
Для канонического сравнения строк используются методы compareTo() и compareToIgnoreCase(). Второй игнорирует регистр символов при сравнении. Эти методы возвращают целое значение int: меньше нуля, если строка меньше заданной; ноль, если строки равны; больше нуля, если исходная строка больше заданной.
Сравнивать можно не только целые строки, но и их части. Это можно сделать при помощи следующих методов:
boolean regionMatches(int start, String s, int sstart, int count) — возвращает true, если все count символов исходной строки с позиции start совпадают со всем символами заданной строки, начиная с позиции sstart.
boolean regionMatches(boolean ignoreCase, int start, String s, int sstart, int count) — все аналогично, с той лишь разницей, что ignoreCase задает, игнорировать регистр или нет.
Отметим также еще несколько операций для проверки содержимого конца и начала строки:
boolean startsWith(String s, int start) — возвращает true, если, начиная с позиции start, строка содержит подстроку s;
boolean startsWith(String s) — аналогично, если start = 0;
boolean endWith(String s) — возвращает true, если строка оканчивается на s.
Одна из наиболее часто используемых функций — это выделение подстроки из строки. Разумеется, разработчики не могли не реализовать метод substring():
String substring(int begin) — возвращает подстроку, содержащую все символы, начиная с позиции begin и до конца текущей строки;
String substring(int begin, int end) — возвращает подстроку, содержащую символы от позиции begin до end.
Так, из строки strTmp можно выделить подстроку "Unicode", например:
strTmp.substring(13); — // учитывая, что strTmp имеет значение "Это строка в Unicode".
Далее следуют еще несколько весомых методов для обработки строк:
String replace(char co, char cn) — производит замену всех символов в строке на символ cn;
String trim() — очень полезный метод. Предназначен для удаления всех пробелов в начале и конце строки;
String toLowerCase() — по возможности, в соответствии с текущей локалью, переводит символы строки в нижний регистр и возвращает новый объект-строку;
String toUpperCase() — то же, только символы приводятся к верхнему регистру;
String toLowerCase(Local l) — аналогично toLowerCase(), только с заданием локали;
String toUpperCase(Local l) — аналогично toUpperCase(), только с заданием локали.
Вот, наверное, все основные методы для обработки строк. Перейдем к проблеме преобразования строк.
Java, можно сказать, строго типизированный язык программирования. Но это только по определению. Естественно, что до turbo pascal'евской типизации Java не опустился. В Java предусмотрено огромное количество способов и методов преобразования одного типа в другой. Первое — это обычное C-шное приведение для простых типов:
byte b;
char c = (char) b;
Дополнительные возможности предоставляют нам мета-классы (или классы-оболочки простых типов). Для того чтобы преобразовать какой-нибудь скалярный простой тип в строку, можно воспользоваться методом valueOf() класса String. Этот метод имеет множество перегруженных вариаций и может преобразовывать типы: boolean, byte, short, int, long, float, double. Например:
boolean bTmp = true;
String s = String.valueOf(bTmp);
Методы valueOf() объявлены как статические, и поэтому нам нет необходимости создавать экземпляр класса.
Обратное преобразование, из строки в значение простого типа, реализовано ничуть не сложнее. Для этого можно воспользоваться методом parse<Type> () класса-оболочки этого типа, где вместо <Type> будет имя типа. Этот метод также статический. Приведем несколько примеров:
byte b = Byte.parseByte("10");
short shrt = Short.parseShort("1234");
long lng = Long.parseLong("1234567");
float flt = Float.parseFloat("1.123");
double dbl = Double.parseDouble("1.123");
У класса-оболочки Integer метод носит имя parseInt(). Также исключения из общего провила составляет тип boolean. Для преобразования из строки в boolean действовать можно так:
boolean b = new Boolean(String).booleanValue();
Если преобразовать строку в число заданного типа не представляется возможным, выбрасывается исключение NumberFormatException.
При трансляции массивов символов и байтов в строку и обратно нужно использовать специально отведенные конструкторы String() и методы getChars() и getBytes() соответственно.
String(char[] chars, int start, int count) — создает объект класса String на основе count подряд идущих элементов массива символов, начиная от элемента с номером start;
String(char[] chars) — если start = 0, а count = chars.length.
При обратном преобразовании используются следующие методы:
char[] toCharArray() — возвращает массив символов, составленный из символов строки;
void getChars(int sb, int se, char[] chars, int cb) — копирует подстроку, ограниченную индексами справа sb, слева se, в массив chars, начиная с элемента cb в массиве;
String(byte[] bytes, int start, int count) — то же, как и с массивами типа char, только здесь типа byte;
String(byte[] bytes) — ... start = 0, count = bytes.length;
String(byte[] bytes, int start, int count, String xlt) — использовать кодировку xlt;
String(byte[] bytes, String xlt) — использовать кодировку xlt;
byte[] getBytes() — преобразует строку в массив byte и возвращает его;
byte[] getBytes(String xlt) — преобразует строку в массив byte, используя кодировку xlt, и возвращает его.
На этом обзор класса String можно завершить. В следующей статье, посвященной обработке строк, мы остановимся на обзоре таких немаловажных классов, как StringBuffer и StringTokenizer.
Алексей Литвинюк (c) litvinuke@tut.by
www.litvinuke.hut.ru
Начнем с общеизвестного факта: любая символьная информация в Java представляется в Unicode. Это означает, что на каждый символ отводится не один, а два байта (16 бит). Любую последовательность символов в ASCII Java будет преобразовывать в Unicode. Однако сие не означает, что Java не работает с 8-битовыми последовательностями. Существует множество способов принудительного преобразования строк в байтовые массивы (byte[]), а также символов (char) в байты (byte) и наоборот.
Cоздать строку можно, например, вот так:
String strTmp = "Это строка в Unicode";
String strTmp = new String("Это строка в Unicode");
Оба этих способа аналогичны. Оба описывают переменную-ссылку типа String, которая указывает на только что созданный объект класса String. Первый варинт был принят для краткости.
Примеры, приведенные выше, создают строчки, одновременно их инициализируя, т.е. присваивая начальное значение "Это строка в Unicode". Помимо конструктора String(java.lang.String), также имеет место быть просто String(). Т.е. без задания начальной инициализации. Это иногда бывает полезно.
String strTmp = new String();
Важно понимать, что экземпляр класса String ни в коем случае не есть массив символов. Т.е. String и char[] — это абсолютно разные вещи и не стоит проводить аналогии с C. Поэтому к String не применим оператор [] для взятия символа с заданным индексом. Этим целям служит метод класса String charAt(int). Принимая целое значение индекса символа в строке, он возращает этот символ. Индексация строки начинается, естественно, с нуля.
char c = strTmp.charAt(0);
Если значение индекса превосходит значение длины строки, иными словами — выходит за границы длины строки, то выбрасывается исключение IndexOutOfBoundsException. Узнать длину строки позволяет метод length(). Обратите внимание, что это именно метод, в отличие от поля length в массивах, и его надо записывать с круглыми скобками.
Обратным целям служит серия методов для определения позиции символа в строке indexOf(). Методы возвращают индекс найденного вхождения символа или подстроки, либо -1 в случае неудачи.
int indexOf(char c) — первое вхождение символа ch, начиная с начала строки;
int indexOf(char c, int start) — первое вхождение символа ch, начиная с начала строки в позиции не меньше start;
int indexOf(String s) — первое вхождение подстроки s, начиная с начала строки;
int indexOf(String s, int start) — первое вхождение подстроки s, начиная с начала строки в позиции не меньше start;
int lastIndexOf(char c) — первое вхождение символа ch, начиная с конца строки;
int lastIndexOf(char c, int start) — первое вхождение символа ch, начиная с конца строки в позиции не больше start;
int lastIndexOf(String s) — первое вхождение подстроки s, начиная с конца строки;
int lastIndexOf(String s, int start) — первое вхождение подстроки s, начиная с конца строки в позиции не больше start.
Отдельной темы заслуживает конкатенация (склеивание) строк. В самом банальном варианте, чтобы добавить к одной строке другую, необходимо воспользоваться методом concat(java.lang.String), например:
strTmp.concat("подстрока 1").concat("подстрока 2").concat("подстрока 3");
Здесть к strTmp мы добавили три строчки. Аналогичную операцию можно было провести иначе:
strTmp += "подстрока 1" + "подстрока 2" + "подстрока 3";
Изначально в Java нет возможности для перегрузки операторов, в отличие от C++, например. Но операция склеивания строк встречается настолько часто, что разработчики решили облегчить труд программистов и сделать исключение. Они сами перегрузили опереторы += и + для объектов класса String, чем мы с удовольствием теперь пользуемся.
Для любой последовательности символов, заключенной в двойные кавычки, Java создает отдельный экземпляр класса String, поэтому не имеет значения, складываете ли вы константные строки, либо это переменные. Причем переменные могут быть разного типа, благодаря приведению "на лету".
Так как строки представленны в виде классов, было бы неправильно производить сравнение двух объектов на предмет совпадения значений строк обычным оператором ==. Т.к. в этом случае сравнивались бы объекты непосредственно. По этой причине для сравнения строк были введены методы equals() и equalsIgnoreCase(). Второй метод, в отличие от первого, игнорирует регистр символов:
String strTmp2 = "asdf";
if (!strTmp.equals(strTmp2)) System.out.println("Не совпадают!");
Для канонического сравнения строк используются методы compareTo() и compareToIgnoreCase(). Второй игнорирует регистр символов при сравнении. Эти методы возвращают целое значение int: меньше нуля, если строка меньше заданной; ноль, если строки равны; больше нуля, если исходная строка больше заданной.
Сравнивать можно не только целые строки, но и их части. Это можно сделать при помощи следующих методов:
boolean regionMatches(int start, String s, int sstart, int count) — возвращает true, если все count символов исходной строки с позиции start совпадают со всем символами заданной строки, начиная с позиции sstart.
boolean regionMatches(boolean ignoreCase, int start, String s, int sstart, int count) — все аналогично, с той лишь разницей, что ignoreCase задает, игнорировать регистр или нет.
Отметим также еще несколько операций для проверки содержимого конца и начала строки:
boolean startsWith(String s, int start) — возвращает true, если, начиная с позиции start, строка содержит подстроку s;
boolean startsWith(String s) — аналогично, если start = 0;
boolean endWith(String s) — возвращает true, если строка оканчивается на s.
Одна из наиболее часто используемых функций — это выделение подстроки из строки. Разумеется, разработчики не могли не реализовать метод substring():
String substring(int begin) — возвращает подстроку, содержащую все символы, начиная с позиции begin и до конца текущей строки;
String substring(int begin, int end) — возвращает подстроку, содержащую символы от позиции begin до end.
Так, из строки strTmp можно выделить подстроку "Unicode", например:
strTmp.substring(13); — // учитывая, что strTmp имеет значение "Это строка в Unicode".
Далее следуют еще несколько весомых методов для обработки строк:
String replace(char co, char cn) — производит замену всех символов в строке на символ cn;
String trim() — очень полезный метод. Предназначен для удаления всех пробелов в начале и конце строки;
String toLowerCase() — по возможности, в соответствии с текущей локалью, переводит символы строки в нижний регистр и возвращает новый объект-строку;
String toUpperCase() — то же, только символы приводятся к верхнему регистру;
String toLowerCase(Local l) — аналогично toLowerCase(), только с заданием локали;
String toUpperCase(Local l) — аналогично toUpperCase(), только с заданием локали.
Вот, наверное, все основные методы для обработки строк. Перейдем к проблеме преобразования строк.
Java, можно сказать, строго типизированный язык программирования. Но это только по определению. Естественно, что до turbo pascal'евской типизации Java не опустился. В Java предусмотрено огромное количество способов и методов преобразования одного типа в другой. Первое — это обычное C-шное приведение для простых типов:
byte b;
char c = (char) b;
Дополнительные возможности предоставляют нам мета-классы (или классы-оболочки простых типов). Для того чтобы преобразовать какой-нибудь скалярный простой тип в строку, можно воспользоваться методом valueOf() класса String. Этот метод имеет множество перегруженных вариаций и может преобразовывать типы: boolean, byte, short, int, long, float, double. Например:
boolean bTmp = true;
String s = String.valueOf(bTmp);
Методы valueOf() объявлены как статические, и поэтому нам нет необходимости создавать экземпляр класса.
Обратное преобразование, из строки в значение простого типа, реализовано ничуть не сложнее. Для этого можно воспользоваться методом parse<Type> () класса-оболочки этого типа, где вместо <Type> будет имя типа. Этот метод также статический. Приведем несколько примеров:
byte b = Byte.parseByte("10");
short shrt = Short.parseShort("1234");
long lng = Long.parseLong("1234567");
float flt = Float.parseFloat("1.123");
double dbl = Double.parseDouble("1.123");
У класса-оболочки Integer метод носит имя parseInt(). Также исключения из общего провила составляет тип boolean. Для преобразования из строки в boolean действовать можно так:
boolean b = new Boolean(String).booleanValue();
Если преобразовать строку в число заданного типа не представляется возможным, выбрасывается исключение NumberFormatException.
При трансляции массивов символов и байтов в строку и обратно нужно использовать специально отведенные конструкторы String() и методы getChars() и getBytes() соответственно.
String(char[] chars, int start, int count) — создает объект класса String на основе count подряд идущих элементов массива символов, начиная от элемента с номером start;
String(char[] chars) — если start = 0, а count = chars.length.
При обратном преобразовании используются следующие методы:
char[] toCharArray() — возвращает массив символов, составленный из символов строки;
void getChars(int sb, int se, char[] chars, int cb) — копирует подстроку, ограниченную индексами справа sb, слева se, в массив chars, начиная с элемента cb в массиве;
String(byte[] bytes, int start, int count) — то же, как и с массивами типа char, только здесь типа byte;
String(byte[] bytes) — ... start = 0, count = bytes.length;
String(byte[] bytes, int start, int count, String xlt) — использовать кодировку xlt;
String(byte[] bytes, String xlt) — использовать кодировку xlt;
byte[] getBytes() — преобразует строку в массив byte и возвращает его;
byte[] getBytes(String xlt) — преобразует строку в массив byte, используя кодировку xlt, и возвращает его.
На этом обзор класса String можно завершить. В следующей статье, посвященной обработке строк, мы остановимся на обзоре таких немаловажных классов, как StringBuffer и StringTokenizer.
Алексей Литвинюк (c) litvinuke@tut.by
www.litvinuke.hut.ru
Компьютерная газета. Статья была опубликована в номере 22 за 2002 год в рубрике программирование :: java