Czy metoda indexOf (String) rozróżnia wielkość liter? Jeśli tak, czy istnieje wersja bez rozróżniania wielkości liter?
Czy metoda indexOf (String) rozróżnia wielkość liter? Jeśli tak, czy istnieje wersja bez rozróżniania wielkości liter?
Odpowiedzi:
We indexOf()
wszystkich metodach rozróżniana jest wielkość liter. Możesz uczynić je (z grubsza, w zepsuty sposób, ale działając w wielu przypadkach) bez rozróżniania wielkości liter, konwertując wcześniej swoje ciągi na duże / małe litery:
s1 = s1.toLowerCase(Locale.US);
s2 = s2.toLowerCase(Locale.US);
s1.indexOf(s2);
"ß".toUpperCase().equals("SS")
Czy metoda indexOf (String) rozróżnia wielkość liter?
Tak, rozróżniana jest wielkość liter:
@Test
public void indexOfIsCaseSensitive() {
assertTrue("Hello World!".indexOf("Hello") != -1);
assertTrue("Hello World!".indexOf("hello") == -1);
}
Jeśli tak, czy istnieje wersja bez rozróżniania wielkości liter?
Nie, nie ma. Możesz przekonwertować oba ciągi na małe litery przed wywołaniem 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))
powinno zwrócić 0, ponieważ pierwszy ciąg jest turecką małą literą "I"
, i dlatego powinien być porównywany jako równy wielkiej litery "I"
w drugim, ale zwraca -1, ponieważ "i"
zamiast tego ta ostatnia jest konwertowana na ).
W klasie StringUtils biblioteki Apache Commons Lang istnieje metoda ignorowania wielkości liter
indexOfIgnoreCase (CharSequence str, CharSequence searchStr)
Tak, indexOf
rozróżnia się wielkość liter.
Najlepszy sposób na niewrażliwość na wielkość liter, jaki znalazłem, to:
String original;
int idx = original.toLowerCase().indexOf(someStr.toLowerCase());
To spowoduje, że wielkość liter nie będzie uwzględniana indexOf()
.
original.toLowerCase().length()
nie zawsze równa się original.length()
. Wynik idx
nie może zostać poprawnie odwzorowany na original
.
Oto moje rozwiązanie, które nie alokuje żadnej pamięci sterty, dlatego powinno być znacznie szybsze niż większość innych wymienionych tutaj implementacji.
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;
}
A oto testy jednostkowe, które weryfikują prawidłowe zachowanie.
@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));
Tak, rozróżniana jest wielkość liter. Możesz nie rozróżniać wielkości liter indexOf
, konwertując String i parametr String na duże litery przed wyszukiwaniem.
String str = "Hello world";
String search = "hello";
str.toUpperCase().indexOf(search.toUpperCase());
Zauważ, że toUpperCase może nie działać w pewnych okolicznościach. Na przykład to:
String str = "Feldbergstraße 23, Mainz";
String find = "mainz";
int idxU = str.toUpperCase().indexOf (find.toUpperCase ());
int idxL = str.toLowerCase().indexOf (find.toLowerCase ());
idxU będzie miało 20 lat, co jest złe! idxL będzie równe 19, co jest poprawne. Przyczyną problemu jest to, że toUpperCase () konwertuje znak „ß” na DWIE znaki, „SS”, co powoduje wyłączenie indeksu.
W związku z tym zawsze trzymaj się toLowerCase ()
find
na "STRASSE"
, nie znajduje go w ogóle w wariancie z małymi literami, ale poprawnie znajduje go w wersji z dużymi literami.
Co robisz ze zwróconą wartością indeksu?
Jeśli używasz go do manipulowania ciągiem, czy nie możesz zamiast tego użyć wyrażenia regularnego?
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);
}
}
Właśnie spojrzałem na źródło. Porównuje znaki, więc rozróżnia wielkość liter.
@Test
public void testIndexofCaseSensitive() {
TestCase.assertEquals(-1, "abcDef".indexOf("d") );
}
Miałem ten sam problem. Wypróbowałem wyrażenia regularne i metodę Apache StringUtils.indexOfIgnoreCase-Method, ale oba były dość powolne ... Więc sam napisałem krótką metodę ...:
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;
}
Według moich testów jest znacznie szybszy ... (przynajmniej jeśli twój ciąg searchString jest raczej krótki). jeśli masz jakieś sugestie dotyczące ulepszeń lub błędów, byłoby miło dać mi znać ... (ponieważ używam tego kodu w aplikacji ;-)
indexOfIgnoreCase("İ","i")
powinien zwrócić 0, ponieważ İ
jest to poprawna wielkość liter i
dla tekstu w języku tureckim, ale zamiast tego zwraca -1, ponieważ i
jest pisane wielką literą do najczęściej używanych I
).
Na pierwsze pytanie udzielono już wielu odpowiedzi. Tak, we String.indexOf()
wszystkich metodach rozróżniana jest wielkość liter.
Jeśli potrzebujesz wrażliwego na ustawienia regionalne, indexOf()
możesz użyć Collator . W zależności od ustawionej wartości siły, możesz uzyskać porównanie bez rozróżniania wielkości liter, a także traktować litery akcentowane jako takie same, jak te bez akcentu itp. Oto przykład, jak to zrobić:
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;
}
Ale nie jest trudno napisać jedną:
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());
}
}
"ı"
jest to wariant pisany małymi literami (tylko nie domyślny w większości języków) "I"
. Lub alternatywnie, jeśli zostanie uruchomiony na komputerze z ustawieniami narodowymi, w których "ı"
jest to ustawienie domyślne, nie zauważy, że "i"
jest to również wariant z małymi literami "I"
.
Konwersja obu ciągów na małe litery zwykle nie jest wielkim problemem, ale byłaby wolna, gdyby niektóre ciągi były długie. A jeśli zrobisz to w pętli, byłoby naprawdę źle. Z tego powodu polecam 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;
}
}
Oto wersja bardzo przypominająca wersję 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;
}
Chciałbym zgłosić roszczenie do JEDNEGO i jedynego opublikowanego do tej pory rozwiązania, które faktycznie działa. :-)
Trzy klasy problemów, z którymi należy się uporać.
Nieprzechodnie reguły dopasowania dla małych i wielkich liter. W innych odpowiedziach często wspominano o tureckim problemie I. Zgodnie z komentarzami w źródle Androida dla String.regionMatches, gruzińskie reguły porównawcze wymagają dodatkowej konwersji na małe litery podczas porównywania pod kątem równości bez uwzględniania wielkości liter.
Przypadki, w których duże i małe litery mają różną liczbę liter. W takich przypadkach prawie wszystkie opublikowane do tej pory rozwiązania zawodzą. Przykład: Niemiecki STRASSE vs Straße mają równość bez rozróżniania wielkości liter, ale mają różne długości.
Wiążące mocne strony znaków akcentowanych. Ustawienia regionalne i kontekst wpływają niezależnie od tego, czy akcenty są zgodne, czy nie. W języku francuskim wielką literą „é” jest „E”, chociaż pojawia się ruch w kierunku używania wielkich liter z akcentami. W kanadyjskim francuskim, wielką literą „é” jest bez wyjątku „É”. Użytkownicy w obu krajach oczekiwaliby, że podczas wyszukiwania „e” będzie pasować do „é”. To, czy znaki akcentowane i bez akcentów pasują do siebie, zależy od języka. Rozważmy teraz: czy „E” równa się „É”? Tak. To robi. W każdym razie we francuskich lokalizacjach.
Obecnie używam android.icu.text.StringSearch
do poprawnej implementacji poprzednich implementacji operacji indexOf bez uwzględniania wielkości liter.
Użytkownicy systemów innych niż Android mogą uzyskać dostęp do tych samych funkcji za pośrednictwem pakietu ICU4J, używając com.ibm.icu.text.StringSearch
klasy.
Uważaj, aby odwoływać się do klas w odpowiednim pakiecie icu ( android.icu.text
lub com.ibm.icu.text
), ponieważ zarówno system Android, jak i środowisko JRE mają klasy o tej samej nazwie w innych przestrzeniach nazw (np. 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();
....
}
Przypadki testowe (ustawienia regionalne, wzorzec, tekst docelowy, oczekiwany wynik):
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: O ile potrafię najlepiej określić, siła wiązania PODSTAWOWA powinna działać właściwie, gdy reguły specyficzne dla lokalizacji rozróżniają znaki akcentowane i nieakcentowane zgodnie z regułami słownikowymi; ale nie wiem, którego języka użyć do przetestowania tej przesłanki. Podarowane przypadki testowe będą wdzięczne.
indexOf rozróżnia wielkość liter. Dzieje się tak, ponieważ używa metody equals do porównywania elementów na liście. To samo dotyczy zawiera i usuwa.