справка о том, как работает String.substring(int, int)

#java #eclipse

#java #eclipse

Вопрос:

приведенный ниже код взят из задания в моем учебнике для простой программы, которая принимает введенное пользователем имя и выводит заглавными буквами первую букву имени и фамилии. Код работает правильно, но я не понимаю, почему name.substring() работает правильно. В частности, меня интересует, как работает блок из строк 24-29. Если пользователь вводит имя «Джонни Джонсон», то i в строке 29 должно содержаться значение 7. Если i действительно содержит 7, то не должно name = name.substring(0, i) содержать «Johnny J», что должно заставить строку 29 фактически хранить «Johnny JJohnson» в String name ? Но вместо этого он фактически сохраняет «Johnny Johnson», как и должно быть.

Мой второй вопрос возникает из-за того, что я возился с этим кодом, чтобы увидеть разные результаты. Если я изменю первую часть строки 29 на name = name.substring(0, i-1) , я получу ошибку (используя Eclipse):

Исключение в потоке «main» java.lang.Исключение StringIndexOutOfBoundsException: индекс строки выходит за пределы диапазона: 15 в java.lang.String.charAt(String.java:558) в RepairName.main(RepairName.java:17)

Почему ошибка появляется в строке 17 вместо строки 29? На самом деле, почему я вообще получаю ошибку, потому что i-1 на самом деле не меняется значение i correct? Я предположил, что это как-то связано с циклом, но поскольку значение i не было изменено, я не знал, почему это могло произойти.

Извините, если это был многословный вопрос. Я новичок в Java и довольно новичок в программировании (очевидно), но я ценю любую информацию, которую вы все можете дать. Спасибо!

  1  import javax.swing.*;
 2  
 3  public class RepairName 
 4  {
 5  public static void main(String[] args)
 6  {
 7      String name, saveOriginalName;
 8          int stringLength;
 9          int i;
10          char c;
11          name = JOptionPane.showInputDialog(null, "Please enter your first and last name");
12      
13          saveOriginalName = name;
14          stringLength = name.length();
15          for (i = 0; i < stringLength; i  )
16          {
17                  c = name.charAt(i);
18                  if (i == 0)
19                  {
20                          c = Character.toUpperCase(c);
21                          name = c   name.substring(1, stringLength);
22                  }
23                  else
24                      if(name.charAt(i) == ' ')
25                          {
26                              i  ;
27                                  c = name.charAt(i);
28                                  c = Character.toUpperCase(c);
29                                  name = name.substring(0, i)   c   name.substring(i 1, stringLength);
30                          }
31          }
32          JOptionPane.showMessageDialog(null, "Original name was "   saveOriginalName   "nRepaired name is "   name);
33 }
34
35 }
  

Ответ №1:

Из String.subString(int, int) javadoc:

 public String substring(int beginIndex, int endIndex)
  

Возвращает новую строку, которая является
подстрока этой строки. The
подстрока начинается с указанного
beginIndex и распространяется на
символ в конце индекса index — 1. Таким образом
длина подстроки равна
endIndex-beginIndex.

Вот ссылка:http://download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html#substring (int, int)

Если сомневаетесь, посмотрите на javadocs 😀

Что касается вашего второго вопроса, опять же javadocs ( http://download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html#charAt (int) ) за charAt(int) помощь вам:

Выдает исключение IndexOutOfBoundsException — если аргумент index отрицателен или не меньше длины этой строки.

При использовании i-1 в substring вы уменьшаете размер имени на 1 каждый раз, когда оно находит ' ' . Это означает, что оно будет повторяться 15 раз, но после того, как name будет найдено, ' ' будет всего 14 символов.

Комментарии:

1. Большое спасибо за объяснение этого! Я даже не думал о первой итерации цикла… Я как раз думал о последнем. Мне нужно многому научиться. спасибо за ссылку

2. Небольшое примечание: теперь у нас есть 1.6 (но substring, конечно, не изменился).

3. Верно, лол, я только что погуглил java string api и получил первое попадание: D

4. как значение i изменяется на -1 ? name = name.substring(0, i - 1) не делает то же самое с i as i = i - 1 или i-- делает это?

5. @knobcreekman если вы измените его на i-1, вы сократите name на один символ, поскольку stringLength каждый цикл не вычисляется, он будет повторять символ, который не существует (в вашем примере 15 итераций, но только 14 символов, следовательно, почему он говорит, что индекс строки вне диапазона: 15) также изменен ответ.

Ответ №2:

Чтобы процитировать substring Javadoc,

Подстрока начинается с указанного beginIndex и продолжается до символа в индексе endIndex — 1.

Другими словами, endIndex не включается в результат.

Что касается вашего второго вопроса, это связано с тем, что вы уменьшаете длину name внутри цикла. Ваше условие цикла ( i < stringLength ) и тот факт, на который вы смотрите, name.charAt(i) предполагают, что длина name остается постоянной (или, по крайней мере, не уменьшается). Это нарушается в тот момент, когда вы начинаете укорачивать строку.

Ответ №3:

Привет, вы получаете исключение, потому что вы выполняете итерацию по строке, но в цикле for вы фактически меняете длину строки name = name.substring(0, i) c name.substring(i 1, StringLength); Таким образом, пока вы все еще находитесь в цикле, размер имени больше не равен старой длине строки.

Boro

Ответ №4:

 Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 15 at java.lang.String.charAt(String.java:558) at RepairName.main(RepairName.java:17)
  

Происходит потому, что вы изменяете name в строках 21 и 29 и сохраняете длину name одинаковой в stringLength

Ответ №5:

объявление 1) javadocs — очень хорошая ссылка, вот java.lang.String.substring (…):

подстрока

 public String substring(int beginIndex, int endIndex)
  

Возвращает новую строку, которая является подстрокой этой строки. The
подстрока начинается с указанного
beginIndex и распространяется на
символ в конце индекса index — 1. Таким образом
длина подстроки равна
endIndex-beginIndex.

Преимущество исключения индекса конца заключается в том, что вы можете последовательно перебирать значения:

 a [0] = x.substring (0, 5);
a [1] = x.substring (5, 10);
a [2] = x.substring (10, 15);

// or 

a [i] = x.substring (i*5, (i 1)*5);
  

Комментарии:

1. спасибо за помощь! вторая часть немного выше моего понимания, хотя и смущает

2. Конец одной последовательности (5, 10, …) может быть началом следующей последовательности (…, 5, 10). Другим преимуществом является то, что вы можете использовать String . длина как конечный индекс без -1. Не очень важно, просто удобство.

Ответ №6:

это правильная подстрока, которая работает таким образом:

 public String substring(int beginIndex, int endIndex)
  

Возвращает новую строку, которая является
подстрока этой строки. The
подстрока начинается с указанного
beginIndex и распространяется на
символ в конце индекса index — 1. Таким образом
длина подстроки равна
endIndex-beginIndex.

итак, для substring(0, 7) требуется 0 .. 6 символов из строки


Обратите внимание, что это действительно плохая практика изменять значение iterate в for-loop (i внутри for-loop-body). Если после пробела у вас нет других символов, вы столкнетесь с исключением

Ответ №7:

объявление 2)

Вы не изменяете сам i, но name здесь усекается:

name = name.substring (0, i-1) c name.substring(i 1, длина строки);

но внешнему циклу не сообщается о новой длине.

объявление 3) (Обзор кода):

Вы должны объявлять и инициализировать свои переменные как можно позже и отмечать как можно больше final . Это говорит о том, что вам не нужно беспокоиться о них после их инициализации.

Учитывая этот код:

 import javax.swing.*;

public class RepairName 
{
    public static void main(String[] args)
    {
        final String name = JOptionPane.showInputDialog(null, "Please enter your first and last name");
        final String saveOriginalName = name;
        final int stringLength = name.length ();
        System.out.println (name);
        for (int i = 0; i < stringLength; i  )
        {
                final char c = name.charAt (i);
                if (i == 0)
                {
                        char upper = Character.toUpperCase (c);
                        name = upper   name.substring (1, stringLength);
                }
                else
                    if (c == ' ')
                        {
                            i  ;
                                final char c2 = name.charAt (i);
                                final char upper = Character.toUpperCase (c2);
                                name = name.substring (0, i-1)   upper   name.substring (i 1, stringLength);
                        }
        }
        JOptionPane.showMessageDialog (null, "Original name was "   saveOriginalName   "nRepaired name is "   name);
    }
}
  

вы бы получили сообщение об ошибке, поскольку name является окончательным. Возможно, это помешало бы вам случайно не синхронизироваться с длиной.

изменено, чтобы иметь изменяемое верхнее имя, но повторяющееся по имени:

     String upName = null;
    for (int i = 0; i < stringLength; i  )
    {
            final char c = name.charAt (i);
            if (i == 0)
            {
                    char upper = Character.toUpperCase (c);
                    upName = upper   name.substring (1, stringLength);
            }
            else
                if (c == ' ')
                    {
                        i  ;
                            final char c2 = name.charAt (i);
                            final char upper = Character.toUpperCase (c2);
                            upName = upName.substring (0, i-1)   upper   name.substring (i 1, stringLength);
                    }
    }
    JOptionPane.showMessageDialog (null, "Original name was "   saveOriginalName   "nRepaired name is "   upName);
  

выдает вам вместо ввода «honky tonky» вывод «HonkyTonky», который, возможно, привел бы вас к тому, как работает substring (from, to). 🙂