Учитывает ли регистр в методе indexOf (String)? Если да, то есть ли его версия без учета регистра?
Учитывает ли регистр в методе indexOf (String)? Если да, то есть ли его версия без учета регистра?
Ответы:
Все indexOf()
методы чувствительны к регистру. Вы можете сделать их (грубо говоря, сломанным способом, но работающими во многих случаях) нечувствительными к регистру, предварительно преобразовав ваши строки в верхний / нижний регистр:
s1 = s1.toLowerCase(Locale.US);
s2 = s2.toLowerCase(Locale.US);
s1.indexOf(s2);
"ß".toUpperCase().equals("SS")
Учитывает ли регистр в методе indexOf (String)?
Да, это чувствительно к регистру:
@Test
public void indexOfIsCaseSensitive() {
assertTrue("Hello World!".indexOf("Hello") != -1);
assertTrue("Hello World!".indexOf("hello") == -1);
}
Если да, то есть ли его версия без учета регистра?
Нет, нет. Вы можете преобразовать обе строки в нижний регистр перед вызовом indexOf:
@Test
public void caseInsensitiveIndexOf() {
assertTrue("Hello World!".toLowerCase().indexOf("Hello".toLowerCase()) != -1);
assertTrue("Hello World!".toLowerCase().indexOf("hello".toLowerCase()) != -1);
}
"ı".toLowerCase(Locale.US).indexOf("I".toLowerCase(Locale.US))
должен возвращать 0, потому что первая строка является турецкой строчной буквой "I"
, и поэтому должен сравниваться как верхний регистр "I"
во втором, но возвращает -1, потому что последний "i"
вместо этого преобразуется в ).
В классе StringUtils библиотеки Apache Commons Lang есть метод игнорирования регистра.
indexOfIgnoreCase (строка CharSequence, строка поиска CharSequence)
Да, с indexOf
учетом регистра.
Я нашел лучший способ сделать регистр нечувствительным:
String original;
int idx = original.toLowerCase().indexOf(someStr.toLowerCase());
Это будет нечувствительно к регистру indexOf()
.
original.toLowerCase().length()
не всегда равно original.length()
. Результат idx
не может быть корректно сопоставлен с original
.
Вот мое решение, которое не выделяет память кучи, поэтому оно должно быть значительно быстрее, чем большинство других реализаций, упомянутых здесь.
public static int indexOfIgnoreCase(final String haystack,
final String needle) {
if (needle.isEmpty() || haystack.isEmpty()) {
// Fallback to legacy behavior.
return haystack.indexOf(needle);
}
for (int i = 0; i < haystack.length(); ++i) {
// Early out, if possible.
if (i + needle.length() > haystack.length()) {
return -1;
}
// Attempt to match substring starting at position i of haystack.
int j = 0;
int ii = i;
while (ii < haystack.length() && j < needle.length()) {
char c = Character.toLowerCase(haystack.charAt(ii));
char c2 = Character.toLowerCase(needle.charAt(j));
if (c != c2) {
break;
}
j++;
ii++;
}
// Walked all the way to the end of the needle, return the start
// position that this was found.
if (j == needle.length()) {
return i;
}
}
return -1;
}
А вот модульные тесты, которые проверяют правильность поведения.
@Test
public void testIndexOfIgnoreCase() {
assertThat(StringUtils.indexOfIgnoreCase("A", "A"), is(0));
assertThat(StringUtils.indexOfIgnoreCase("a", "A"), is(0));
assertThat(StringUtils.indexOfIgnoreCase("A", "a"), is(0));
assertThat(StringUtils.indexOfIgnoreCase("a", "a"), is(0));
assertThat(StringUtils.indexOfIgnoreCase("a", "ba"), is(-1));
assertThat(StringUtils.indexOfIgnoreCase("ba", "a"), is(1));
assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", " Royal Blue"), is(-1));
assertThat(StringUtils.indexOfIgnoreCase(" Royal Blue", "Royal Blue"), is(1));
assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "royal"), is(0));
assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "oyal"), is(1));
assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "al"), is(3));
assertThat(StringUtils.indexOfIgnoreCase("", "royal"), is(-1));
assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", ""), is(0));
assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "BLUE"), is(6));
assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "BIGLONGSTRING"), is(-1));
assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "Royal Blue LONGSTRING"), is(-1));
}
assertThat(StringUtils.indexOfIgnoreCase("ı" /* Turkish lower-case I, U+0131 */, "I"), is(0));
Да, это с учетом регистра. Вы можете сделать регистр нечувствительным indexOf
, преобразовав строку и параметр String в верхний регистр перед поиском.
String str = "Hello world";
String search = "hello";
str.toUpperCase().indexOf(search.toUpperCase());
Обратите внимание, что toUpperCase может не работать в некоторых случаях. Например это:
String str = "Feldbergstraße 23, Mainz";
String find = "mainz";
int idxU = str.toUpperCase().indexOf (find.toUpperCase ());
int idxL = str.toLowerCase().indexOf (find.toLowerCase ());
idxU будет 20, что неверно! idxL будет 19, что правильно. Проблема заключается в том, что toUpperCase () преобразует символ «ß» в ДВА символа, «SS», и это сбрасывает индекс.
Следовательно, всегда используйте toLowerCase ().
find
на "STRASSE"
, он вообще не найдет его в нижнем регистре, но правильно найдет его в верхнем регистре.
Что вы делаете с возвращенным значением индекса?
Если вы используете его для управления своей строкой, не могли бы вы вместо этого использовать регулярное выражение?
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class StringIndexOfRegexpTest {
@Test
public void testNastyIndexOfBasedReplace() {
final String source = "Hello World";
final int index = source.toLowerCase().indexOf("hello".toLowerCase());
final String target = "Hi".concat(source.substring(index
+ "hello".length(), source.length()));
assertEquals("Hi World", target);
}
@Test
public void testSimpleRegexpBasedReplace() {
final String source = "Hello World";
final String target = source.replaceFirst("(?i)hello", "Hi");
assertEquals("Hi World", target);
}
}
Я только что посмотрел на источник. Он сравнивает символы, поэтому он чувствителен к регистру.
@Test
public void testIndexofCaseSensitive() {
TestCase.assertEquals(-1, "abcDef".indexOf("d") );
}
Была такая же проблема. Я пробовал регулярное выражение и apache StringUtils.indexOfIgnoreCase-Method, но оба были довольно медленными ... Поэтому я сам написал короткий метод ...:
public static int indexOfIgnoreCase(final String chkstr, final String searchStr, int i) {
if (chkstr != null && searchStr != null && i > -1) {
int serchStrLength = searchStr.length();
char[] searchCharLc = new char[serchStrLength];
char[] searchCharUc = new char[serchStrLength];
searchStr.toUpperCase().getChars(0, serchStrLength, searchCharUc, 0);
searchStr.toLowerCase().getChars(0, serchStrLength, searchCharLc, 0);
int j = 0;
for (int checkStrLength = chkstr.length(); i < checkStrLength; i++) {
char charAt = chkstr.charAt(i);
if (charAt == searchCharLc[j] || charAt == searchCharUc[j]) {
if (++j == serchStrLength) {
return i - j + 1;
}
} else { // faster than: else if (j != 0) {
i = i - j;
j = 0;
}
}
}
return -1;
}
Согласно моим тестам, он намного быстрее ... (по крайней мере, если ваш searchString довольно короткий). если у вас есть предложения по улучшению или ошибки, было бы неплохо сообщить мне ... (поскольку я использую этот код в приложении ;-)
indexOfIgnoreCase("İ","i")
должен возвращать 0, потому что İ
это правильное использование заглавных букв i
для турецкого текста, но вместо этого возвращает -1, потому что i
используется заглавная буква для более распространенного I
).
На первый вопрос уже много раз ответили. Да, все String.indexOf()
методы чувствительны к регистру.
Если вам нужен чувствительный к языку, indexOf()
вы можете использовать Collator . В зависимости от установленного вами значения силы вы можете получить сравнение без учета регистра, а также рассматривать буквы с диакритическими знаками так же, как и буквы без диакритических знаков и т. Д. Вот пример того, как это сделать:
private int indexOf(String original, String search) {
Collator collator = Collator.getInstance();
collator.setStrength(Collator.PRIMARY);
for (int i = 0; i <= original.length() - search.length(); i++) {
if (collator.equals(search, original.substring(i, i + search.length()))) {
return i;
}
}
return -1;
}
Но написать одно несложно:
public class CaseInsensitiveIndexOfTest extends TestCase {
public void testOne() throws Exception {
assertEquals(2, caseInsensitiveIndexOf("ABC", "xxabcdef"));
}
public static int caseInsensitiveIndexOf(String substring, String string) {
return string.toLowerCase().indexOf(substring.toLowerCase());
}
}
"ı"
это вариант в нижнем регистре (только не по умолчанию для большинства языков) "I"
. Или же, если работать на множество машин на местность , где "ı"
есть по умолчанию, он будет не в состоянии заметить , что "i"
также является строчным вариантом "I"
.
Преобразование обеих строк в нижний регистр обычно не представляет большого труда, но это будет медленным, если некоторые из строк будут длинными. И если вы сделаете это в цикле, это будет очень плохо. По этой причине я бы рекомендовал indexOfIgnoreCase
.
static string Search(string factMessage, string b)
{
int index = factMessage.IndexOf(b, StringComparison.CurrentCultureIgnoreCase);
string line = null;
int i = index;
if (i == -1)
{ return "not matched"; }
else
{
while (factMessage[i] != ' ')
{
line = line + factMessage[i];
i++;
}
return line;
}
}
Вот версия, очень похожая на версию Apache StringUtils:
public int indexOfIgnoreCase(String str, String searchStr) {
return indexOfIgnoreCase(str, searchStr, 0);
}
public int indexOfIgnoreCase(String str, String searchStr, int fromIndex) {
// /programming/14018478/string-contains-ignore-case/14018511
if(str == null || searchStr == null) return -1;
if (searchStr.length() == 0) return fromIndex; // empty string found; use same behavior as Apache StringUtils
final int endLimit = str.length() - searchStr.length() + 1;
for (int i = fromIndex; i < endLimit; i++) {
if (str.regionMatches(true, i, searchStr, 0, searchStr.length())) return i;
}
return -1;
}
Я хотел бы заявить права на ЕДИНОЕ и единственное решение, опубликованное на данный момент, которое действительно работает. :-)
Три класса проблем, которые нужно решить.
Нетранзитивные правила сопоставления для нижнего и верхнего регистра. Турецкая проблема I часто упоминалась в других ответах. Согласно комментариям в источнике Android для String.regionMatches, для грузинских правил сравнения требуется дополнительное преобразование в нижний регистр при сравнении на равенство без учета регистра.
Случаи, когда в формах верхнего и нижнего регистра разное количество букв. Практически все решения, опубликованные до сих пор, терпят неудачу в этих случаях. Пример: немецкие STRASSE и Straße имеют равенство без учета регистра, но имеют разную длину.
Сила связывания акцентированных персонажей. Локаль И контекст влияет независимо от того, совпадают ли акценты. Во французском языке заглавная форма «é» - это «E», хотя наблюдается тенденция к использованию заглавных акцентов. В канадском французском языке заглавная форма «é» - это «É» без исключения. Пользователи в обеих странах ожидают, что "e" будет соответствовать "é" при поиске. Совместимость символов с диакритическими знаками и без диакритических знаков зависит от региона. Теперь подумайте: равно ли "E" "É"? Да. Оно делает. Во всяком случае, во французских странах.
В настоящее время я использую android.icu.text.StringSearch
для правильной реализации предыдущих реализаций операций indexOf без учета регистра.
Пользователи, не использующие Android, могут получить доступ к тем же функциям через пакет ICU4J, используя com.ibm.icu.text.StringSearch
класс.
Будьте осторожны, чтобы ссылаться на классы в правильном пакете icu ( android.icu.text
или com.ibm.icu.text
), поскольку Android и JRE имеют классы с одинаковыми именами в других пространствах имен (например, Collator).
this.collator = (RuleBasedCollator)Collator.getInstance(locale);
this.collator.setStrength(Collator.PRIMARY);
....
StringSearch search = new StringSearch(
pattern,
new StringCharacterIterator(targetText),
collator);
int index = search.first();
if (index != SearchString.DONE)
{
// remember that the match length may NOT equal the pattern length.
length = search.getMatchLength();
....
}
Тестовые случаи (языковой стандарт, шаблон, целевой текст, ожидаемый результат):
testMatch(Locale.US,"AbCde","aBcDe",true);
testMatch(Locale.US,"éèê","EEE",true);
testMatch(Locale.GERMAN,"STRASSE","Straße",true);
testMatch(Locale.FRENCH,"éèê","EEE",true);
testMatch(Locale.FRENCH,"EEE","éèê",true);
testMatch(Locale.FRENCH,"éèê","ÉÈÊ",true);
testMatch(new Locale("tr-TR"),"TITLE","tıtle",true); // Turkish dotless I/i
testMatch(new Locale("tr-TR"),"TİTLE","title",true); // Turkish dotted I/i
testMatch(new Locale("tr-TR"),"TITLE","title",false); // Dotless-I != dotted i.
PS: Насколько я могу судить, сила привязки PRIMARY должна действовать правильно, когда правила, специфичные для локали, различают символы с диакритическими знаками и без них в соответствии с правилами словаря; но я не знаю, какой язык использовать для проверки этой предпосылки. Мы будем благодарны за предоставленные тестовые примеры.
indexOf чувствителен к регистру. Это потому, что он использует метод equals для сравнения элементов в списке. То же самое касается "содержать" и "удалить".