Чи чутливий до методу 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 str, CharSequence searchStr)
Так, 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 на регістр перед пошуком.
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") );
}
Була та сама проблема. Я спробував регулярний вираз та апаш 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;
}
}
Ось версія, що дуже нагадує версію StringUtils від Apache:
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: Якнайкраще я можу визначити, ПРИМАРНА міцність прив’язки повинна робити правильно, коли правила, характерні для локалі, розмежовують наголошені та ненаголошені символи відповідно до правил словника; але я не знаю, яку локаль використовувати для перевірки цієї передумови. Подаровані тестові випадки будуть вдячні.
indexOf чутливий до регістру. Це пов’язано з тим, що він використовує метод equals для порівняння елементів у списку. Те саме стосується вмісту та видалення.