¿El método indexOf (String) distingue entre mayúsculas y minúsculas? Si es así, ¿hay una versión que no distinga entre mayúsculas y minúsculas?
¿El método indexOf (String) distingue entre mayúsculas y minúsculas? Si es así, ¿hay una versión que no distinga entre mayúsculas y minúsculas?
Respuestas:
Los indexOf()
métodos son todas mayúsculas y minúsculas. Puede hacerlos (aproximadamente, de manera discontinua, pero funcionando para muchos casos) que no distingan entre mayúsculas y minúsculas convirtiendo sus cadenas a mayúsculas / minúsculas de antemano:
s1 = s1.toLowerCase(Locale.US);
s2 = s2.toLowerCase(Locale.US);
s1.indexOf(s2);
"ß".toUpperCase().equals("SS")
¿El método indexOf (String) distingue entre mayúsculas y minúsculas?
Sí, distingue entre mayúsculas y minúsculas:
@Test
public void indexOfIsCaseSensitive() {
assertTrue("Hello World!".indexOf("Hello") != -1);
assertTrue("Hello World!".indexOf("hello") == -1);
}
Si es así, ¿hay una versión que no distinga entre mayúsculas y minúsculas?
No, no lo hay. Puede convertir ambas cadenas a minúsculas antes de llamar a 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))
debería devolver 0 porque la primera cadena es minúscula turca "I"
, y por lo tanto debería comparar como igual a la mayúscula "I"
en el segundo, pero devuelve -1 porque el último se convierte en su "i"
lugar).
Hay un método de caso ignorado en la clase StringUtils de la biblioteca Apache Commons Lang
indexOfIgnoreCase (CharSequence str, CharSequence searchStr)
Si, indexOf
distingue entre mayúsculas y minúsculas.
La mejor manera de hacer insensibilidad a mayúsculas y minúsculas que he encontrado es:
String original;
int idx = original.toLowerCase().indexOf(someStr.toLowerCase());
Eso no distingue entre mayúsculas y minúsculas indexOf()
.
original.toLowerCase().length()
no siempre es igual a original.length()
. El resultado idx
no se puede asignar correctamente a original
.
Aquí está mi solución que no asigna ninguna memoria de pila, por lo tanto, debería ser significativamente más rápida que la mayoría de las otras implementaciones mencionadas aquí.
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;
}
Y aquí están las pruebas unitarias que verifican el comportamiento correcto.
@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));
Sí, distingue entre mayúsculas y minúsculas. Puede hacer una diferencia entre mayúsculas y minúsculas indexOf
convirtiendo su String y el parámetro String a mayúsculas antes de buscar.
String str = "Hello world";
String search = "hello";
str.toUpperCase().indexOf(search.toUpperCase());
Tenga en cuenta que es posible que toUpperCase no funcione en algunas circunstancias. Por ejemplo esto:
String str = "Feldbergstraße 23, Mainz";
String find = "mainz";
int idxU = str.toUpperCase().indexOf (find.toUpperCase ());
int idxL = str.toLowerCase().indexOf (find.toLowerCase ());
idxU será 20, ¡lo cual está mal! idxL será 19, lo cual es correcto. Lo que está causando el problema es que toUpperCase () convierte el carácter "ß" en DOS caracteres, "SS" y esto arroja el índice.
En consecuencia, quédese siempre con toLowerCase ()
find
a "STRASSE"
, no lo encuentra en absoluto en la variante de minúsculas, pero lo encuentra correctamente en la versión en mayúsculas.
¿Qué está haciendo con el valor del índice una vez devuelto?
Si lo está usando para manipular su cadena, ¿no podría usar una expresión regular en su lugar?
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);
}
}
Acabo de mirar la fuente. Compara los caracteres para que distinga entre mayúsculas y minúsculas.
@Test
public void testIndexofCaseSensitive() {
TestCase.assertEquals(-1, "abcDef".indexOf("d") );
}
Tuvo el mismo problema. Probé la expresión regular y el Apache StringUtils.indexOfIgnoreCase-Method, pero ambos fueron bastante lentos ... Así que escribí un método corto yo mismo ...:
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;
}
Según mis pruebas, es mucho más rápido ... (al menos si su searchString es bastante corto). Si tiene alguna sugerencia de mejora o errores, sería bueno que me lo hiciera saber ... (ya que uso este código en una aplicación ;-)
indexOfIgnoreCase("İ","i")
debe devolver 0 porque İ
es la mayúscula correcta de i
para el texto turco, pero en su lugar devuelve -1 porque i
se escribe en mayúscula con la más común I
).
La primera pregunta ya ha sido respondida muchas veces. Sí, todos los String.indexOf()
métodos distinguen entre mayúsculas y minúsculas.
Si necesita una configuración regional, indexOf()
puede usar el Collator . Dependiendo del valor de fuerza que establezca, puede obtener una comparación que no distingue entre mayúsculas y minúsculas y también tratar las letras acentuadas como si fueran las mismas que las sin acentos, etc. Aquí hay un ejemplo de cómo hacer esto:
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;
}
Pero no es difícil escribir uno:
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());
}
}
"ı"
es una variante en minúscula (pero no la predeterminada en la mayoría de los idiomas) de "I"
. O alternativamente, si se ejecuta en una máquina configurada en una configuración regional donde "ı"
es la predeterminada, no se dará cuenta de que "i"
también es una variante en minúscula de "I"
.
La conversión de ambas cadenas a minúsculas no suele ser un gran problema, pero sería lento si algunas de las cadenas son largas. Y si haces esto en un bucle, sería realmente malo. Por esta razón, lo recomendaría 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;
}
}
Aquí hay una versión que se parece mucho a la versión StringUtils de 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;
}
Me gustaría reclamar la ÚNICA y única solución publicada hasta ahora que realmente funciona. :-)
Tres clases de problemas que deben resolverse.
Reglas de coincidencia no transitivas para minúsculas y mayúsculas. El problema del turco I se ha mencionado con frecuencia en otras respuestas. Según los comentarios en la fuente de Android para String.regionMatches, las reglas de comparación georgianas requieren una conversión adicional a minúsculas cuando se compara para la igualdad que no distingue entre mayúsculas y minúsculas.
Casos en los que las formas en mayúsculas y minúsculas tienen un número diferente de letras. Prácticamente todas las soluciones publicadas hasta ahora fallan, en estos casos. Ejemplo: alemán STRASSE vs. Straße tienen una igualdad que no distingue entre mayúsculas y minúsculas, pero tienen diferentes longitudes.
Puntos fuertes de los personajes acentuados. Efecto de configuración regional Y de contexto independientemente de que los acentos coincidan o no. En francés, la forma mayúscula de 'é' es 'E', aunque hay un movimiento hacia el uso de acentos en mayúsculas. En francés canadiense, la forma mayúscula de 'é' es 'É', sin excepción. Los usuarios de ambos países esperan que "e" coincida con "é" al realizar la búsqueda. La coincidencia de caracteres acentuados y no acentuados depende de la configuración regional. Ahora considere: ¿"E" es igual a "É"? Si. Lo hace. En los lugares franceses, de todos modos.
Estoy usando actualmente android.icu.text.StringSearch
para implementar correctamente implementaciones anteriores de operaciones indexOf que no distinguen entre mayúsculas y minúsculas.
Los usuarios que no utilizan Android pueden acceder a la misma funcionalidad a través del paquete ICU4J, utilizando el com.ibm.icu.text.StringSearch
clase.
Tenga cuidado de hacer referencia a clases en el paquete icu correcto ( android.icu.text
o com.ibm.icu.text
) ya que Android y el JRE tienen clases con el mismo nombre en otros espacios de nombres (por ejemplo, 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();
....
}
Casos de prueba (configuración regional, patrón, texto de destino, resultado esperado):
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.
PD: Lo mejor que puedo determinar, la fuerza de enlace PRIMARY debería hacer lo correcto cuando las reglas específicas de la configuración regional diferencian entre caracteres acentuados y no acentuados según las reglas del diccionario; pero no sé qué configuración regional usar para probar esta premisa. Los casos de prueba donados serán agradecidos.
indexOf distingue entre mayúsculas y minúsculas. Esto se debe a que utiliza el método equals para comparar los elementos de la lista. Lo mismo ocurre con contiene y elimina.