dividir string apenas na primeira instância do caractere especificado


272

No meu código, divido uma string com base _e pego o segundo item na matriz.

var element = $(this).attr('class');
var field = element.split('_')[1];

Pega good_lucke me fornece luck. Funciona bem!

Mas agora tenho uma classe que se parece good_luck_buddy. Como obtenho meu javascript para ignorar o segundo _e me fornecer luck_buddy?

Encontrei isso var field = element.split(new char [] {'_'}, 2);na resposta ac # stackoverflow, mas não funciona. Eu tentei no jsFiddle ...

Respostas:


408

Use a captura de parênteses :

"good_luck_buddy".split(/_(.+)/)[1]
"luck_buddy"

Eles são definidos como

Se separatorcontiver parênteses de captura, os resultados correspondentes serão retornados na matriz.

Portanto, neste caso, queremos dividir em _.+(por exemplo, separador de divisão sendo uma sub-cadeia começando com _), mas também deixar que o resultado contenha uma parte do nosso separador (ou seja, tudo depois _).

Neste exemplo, nosso separador (correspondência _(.+)) é _luck_buddye o grupo capturado (dentro do separador) é lucky_buddy. Sem os parênteses de captura, a luck_buddy(correspondência .+) não teria sido incluída na matriz de resultados, como é o caso de simples splitque separadores não sejam incluídos no resultado.


21
Você nem precisa (?), Basta usar /_(.+)/ para capturar mais 1 caractere após o primeiro _ #
Mark

3
Muito elegante. Funciona como um encanto. Obrigado.
precisa saber é o seguinte

12
Só para esclarecer, o motivo pelo qual essa solução funciona é que tudo depois do primeiro _é correspondido dentro de um grupo de captura e adicionado à lista de tokens por esse motivo.
Alan Moore

28
Alguém sabe por que eu recebo um elemento string vazia extra com esta: em: "Aspect Ratio: 16:9".split(/:(.+)/)a:["Aspect Ratio", " 16:9", ""]
katy Lavallee

4
@katylavallee - Isso pode ajudar: stackoverflow.com/questions/12836062/… Como o separador é ": 16:9", não existe nada após o separador, criando assim a sequência vazia no final.
Derek朕會功夫

232

Para que você precisa de expressões regulares e matrizes?

myString = myString.substring(myString.indexOf('_')+1)

var myString= "hello_there_how_are_you"
myString = myString.substring(myString.indexOf('_')+1)
console.log(myString)


5
string! == String. javascript faz distinção entre maiúsculas e minúsculas.
Kennebec

3
Eu acho que essa é a melhor resposta. também é possível obter string após o segundo _escrevendo:myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 )
muratgozel

9
A resposta gera a segunda parte da string. E se você quiser a primeira parte também? Com var str = "good_luck_buddy", res = str.split(/_(.+)/);você obtém todas as partes:console.log(res[0]); console.log(res[1]);
dom

1
@PeterLeger let split = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ]Aí está. Também com suporte de agulha variável
Steffan

Este é o gênio!
stuckedoverflow

36

Evito o RegExp a todo custo. Aqui está outra coisa que você pode fazer:

"good_luck_buddy".split('_').slice(1).join('_')

18
Quem tem medo do RegExp nunca pode ser informado sobre o quão bom é o RegExp. Você precisa encontrar a porta você mesmo. Quando estiver lá, nunca mais olhará para trás. Pergunte-me novamente em alguns anos e você dirá a mim como é ótimo.
Christiaan Westerbeek

3
@yonas Tome a pílula vermelha!
Frnhr

2
@ yonas Sim, tome a pílula vermelha! Ele vai fazer sua vida mais rápido, mesmo para seqüências curtas: jsperf.com/split-by-first-colon
Julian F. Weinert

15
Ha! Escrevi este comentário há 4 anos. Definitivamente, estou a bordo do RegExp agora! :)
yonas 5/17

3
@ yonas é melhor você não. O RegExp é incrível quando você precisa . Não é o caso aqui. Verifique o teste atualizado: jsperf.com/split-by-first-colon/2
metalim

11

Substitua a primeira instância por um espaço reservado exclusivo e divida a partir daí.

"good_luck_buddy".replace(/\_/,'&').split('&')

["good","luck_buddy"]

Isso é mais útil quando os dois lados da divisão são necessários.


3
Isso coloca uma restrição desnecessária na string.
22616 Yan Foto

Esta resposta funcionou para mim quando todas as respostas acima não.
GuitarViking

1
@YanFoto você quer dizer usando '&'? Poderia ser qualquer coisa.
sebjwallace

2
@sebjwallace O que você escolher, significa que você não pode ter esse caractere na string. Por exemplo, "fish & chips_are_great" fornece [fish, chips, are_great], eu acho.
Joe

@ Joe Você poderia usar qualquer coisa em vez de '&' - era apenas um exemplo. Você pode substituir a primeira ocorrência de _ por ¬, se desejar. Assim, "fish & chips_are_great" iria substituir a primeira ocorrência do _ com ¬ para dar "fish & chips¬are_great", em seguida, dividir por ¬ para obter [ "fish & chips", "are_great"]
sebjwallace

8

Você pode usar a expressão regular como:

var arr = element.split(/_(.*)/)
Você pode usar o segundo parâmetro que especifica o limite da divisão. ou seja: campo var = elemento.split ('_', 1) [1];

6
Isso especifica apenas quantos itens de divisão são retornados, não quantas vezes ele é dividido. 'good_luck_buddy'.split('_', 1);retorna apenas['good']
Alex Vidal

Obrigado assumiu isso. Atualizado a postagem para usar uma expressão regular.
Chandu

Era (:?.*)para ser um grupo que não capturava? Se assim for, deve ser (?:.*), mas se você corrigi-lo, verá que não funciona mais. (:?.*)corresponde a um opcional :seguido por zero ou mais de qualquer caractere. Essa solução acaba funcionando pelo mesmo motivo que o @ MarkF's: tudo após o primeiro _é adicionado à lista de tokens porque foi correspondido em um grupo de captura. (Além disso, o gmodificador não tem efeito quando usado em um regex divisão.)
Alan Moore

Obrigado, não percebi. Atualizado o Regex e tentou fazê-lo ao longo par de scenarion ...
Chandu

1
Ele não funciona no IE8 e eu voltar para indexOf e substring
Igor Alekseev

6

Esta solução funcionou para mim

var str = "good_luck_buddy";
var index = str.indexOf('_');
var arr = [str.slice(0, index), str.slice(index + 1)];

//arr[0] = "good"
//arr[1] = "luck_buddy"

OU

var str = "good_luck_buddy";
var index = str.indexOf('_');
var [first, second] = [str.slice(0, index), str.slice(index + 1)];

//first = "good"
//second = "luck_buddy"

1
No entanto, isso não funciona se o divisor tiver mais de 1 caractere.
haykam 26/01

5

Atualmente String.prototype.split, de fato, você limita o número de divisões.

str.split([separator[, limit]])

...

limite Opcional

Um número inteiro não negativo que limita o número de divisões. Se fornecido, divide a string em cada ocorrência do separador especificado, mas para quando as entradas de limite foram colocadas na matriz. Qualquer texto restante não é incluído na matriz.

A matriz pode conter menos entradas que o limite se o final da string for atingido antes que o limite seja atingido. Se o limite for 0, nenhuma divisão será realizada.

embargo

Pode não funcionar da maneira que você espera. Eu esperava que isso ignorasse o restante dos delimitadores, mas, quando atinge o limite, divide a sequência restante novamente, omitindo a parte após a divisão dos resultados de retorno.

let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C"]

Eu estava esperando por:

let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B_C_D_E"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C_D_E"]

O mesmo aqui. Parece que o PHP está se dividindo em "primeiro" e "resto".
BananaAcid 29/03

4

String.splitInfelizmente, o Javascript não tem como limitar o número real de divisões. Ele tem um segundo argumento que especifica quantos itens de divisão reais são retornados, o que não é útil no seu caso. A solução seria dividir a string, desativar o primeiro item e juntar os itens restantes:

var element = $(this).attr('class');
var parts = element.split('_');

parts.shift(); // removes the first item from the array
var field = parts.join('_');

Vejo que a função de divisão não ajuda, mas o uso de uma regex parece conseguir isso. Ele deve especificar que você está se referindo à própria função Split, nativamente.
Dan Hanly

1
Interessante, essa solução resume o problema em uma solução mais legível / gerenciável. No meu caso de converter um nome completo em primeiro e último (sim, nossos requisitos forçaram essa lógica), essa solução funcionou melhor e ficou mais legível que as outras. Obrigado
Sukima

Isso não é mais verdade :)
Kraken

3

Eu preciso das duas partes da string, então, regex lookbehind me ajudará com isso.

const full_name = 'Maria do Bairro';
const [first_name, last_name] = full_name.split(/(?<=^[^ ]+) /);
console.log(first_name);
console.log(last_name);


3

Com a ajuda da atribuição de desestruturação, pode ser mais legível:

let [first, ...rest] = "good_luck_buddy".split('_')
rest = rest.join('_')

2

Solução mais rápida?

Fiz alguns benchmarks e esta solução ganhou imensamente: 1

str.slice(str.indexOf(delim) + delim.length)

// as function
function gobbleStart(str, delim) {
    return str.slice(str.indexOf(delim) + delim.length);
}

// as polyfill
String.prototype.gobbleStart = function(delim) {
    return this.slice(this.indexOf(delim) + delim.length);
};

Comparação de desempenho com outras soluções

O único candidato próximo era a mesma linha de código, exceto usando em substrvez de slice.

Outras soluções que tentei envolver splitou RegExptiveram um grande impacto no desempenho e foram cerca de 2 ordens de magnitude mais lentas. Usar joinos resultados de split, obviamente, adiciona uma penalidade de desempenho adicional.

Por que eles são mais lentos? Sempre que um novo objeto ou matriz tiver que ser criado, o JS precisará solicitar um pedaço de memória do sistema operacional. Este processo é muito lento.

Aqui estão algumas diretrizes gerais, caso você esteja buscando benchmarks:

  • Novas alocações de memória dinâmica para objetos {}ou matrizes [](como a que splitcria) custarão muito em desempenho.
  • RegExp as pesquisas são mais complicadas e, portanto, mais lentas que as pesquisas de string.
  • Se você já possui uma matriz, a destruição de matrizes é tão rápida quanto explicitamente indexá-las e parece incrível.

Removendo além da primeira instância

Aqui está uma solução que dividirá até a nésima instância. Não é tão rápido, mas, na questão do OP, gobble(element, '_', 1)ainda é> duas vezes mais rápido que uma solução RegExpou splite pode fazer mais:

/*
`gobble`, given a positive, non-zero `limit`, deletes
characters from the beginning of `haystack` until `needle` has
been encountered and deleted `limit` times or no more instances
of `needle` exist; then it returns what remains. If `limit` is
zero or negative, delete from the beginning only until `-(limit)`
occurrences or less of `needle` remain.
*/
function gobble(haystack, needle, limit = 0) {
  let remain = limit;
  if (limit <= 0) { // set remain to count of delim - num to leave
    let i = 0;
    while (i < haystack.length) {
      const found = haystack.indexOf(needle, i);
      if (found === -1) {
        break;
      }
      remain++;
      i = found + needle.length;
    }
  }

  let i = 0;
  while (remain > 0) {
    const found = haystack.indexOf(needle, i);
    if (found === -1) {
      break;
    }
    remain--;
    i = found + needle.length;
  }
  return haystack.slice(i);
}

Com a definição acima, gobble('path/to/file.txt', '/')daria o nome do arquivo e gobble('prefix_category_item', '_', 1)removeria o prefixo como a primeira solução nesta resposta.


  1. Os testes foram executados no Chrome 70.0.3538.110 no macOSX 10.14.

Vamos lá ... É 2019 ... As pessoas lá fora realmente ainda marcam esse tipo de coisa?
Victor Schröder

Concordo. Embora o microbenchmarking seja um pouco interessante, você deve confiar em um compilador ou tradutor para otimizações. Quem sabe. Mb alguém lendo isso está construindo um compilador ou usando ejs / embedded e não pode usar regex. No entanto, isso parece melhor para o meu caso específico do que um regex. (Eu removeria a "solução mais rápida")
TamusJRoyce

1

A solução do Mark F é incrível, mas não é suportada por navegadores antigos. A solução do Kennebec é incrível e é suportada por navegadores antigos, mas não suporta regex.

Portanto, se você estiver procurando uma solução que divida sua string apenas uma vez, suportada por navegadores antigos e que suporte regex, aqui está a minha solução:

String.prototype.splitOnce = function(regex)
{
    var match = this.match(regex);
    if(match)
    {
        var match_i = this.indexOf(match[0]);
        
        return [this.substring(0, match_i),
        this.substring(match_i + match[0].length)];
    }
    else
    { return [this, ""]; }
}

var str = "something/////another thing///again";

alert(str.splitOnce(/\/+/)[1]);


1

Para iniciantes como eu, que não estão acostumados à Expressão Regular, esta solução alternativa funcionou:

   var field = "Good_Luck_Buddy";
   var newString = field.slice( field.indexOf("_")+1 );

O método slice () extrai uma parte de uma string e retorna uma nova string e o método indexOf () retorna a posição da primeira ocorrência encontrada de um valor especificado em uma string.


Esta não é uma solução, mas uma maneira correta de fazê-lo;)
Victor Schröder

1

Use o replace()método string com um regex :

var result = "good_luck_buddy".replace(/.*?_/, "");
console.log(result);

Essa regex corresponde a 0 ou mais caracteres antes do primeiro _e o _próprio. A partida é substituída por uma sequência vazia.


A document.body.innerHTMLparte aqui é completamente inútil.
Victor Schröder

@ VictorSchröder, como você espera ver a saída do snippet sem document.body.innerHTML?
James T

2
document.bodydepende do DOM estar presente e não funcionará em um ambiente JavaScript puro. console.logé suficiente para esse fim ou simplesmente deixa o resultado em uma variável para inspeção.
Victor Schröder

@ VictorSchröder Eu não acho que teria causado muita confusão, mas eu editei mesmo assim.
James T

0

Isso funcionou para mim no Chrome + FF:

"foo=bar=beer".split(/^[^=]+=/)[1] // "bar=beer"
"foo==".split(/^[^=]+=/)[1] // "="
"foo=".split(/^[^=]+=/)[1] // ""
"foo".split(/^[^=]+=/)[1] // undefined

Se você também precisar da chave, tente o seguinte:

"foo=bar=beer".split(/^([^=]+)=/) // Array [ "", "foo", "bar=beer" ]
"foo==".split(/^([^=]+)=/) // [ "", "foo", "=" ]
"foo=".split(/^([^=]+)=/) // [ "", "foo", "" ]
"foo".split(/^([^=]+)=/) // [ "foo" ]

//[0] = ignored (holds the string when there's no =, empty otherwise)
//[1] = hold the key (if any)
//[2] = hold the value (if any)

0

Aqui está um RegExp que faz o truque.

'good_luck_buddy' . split(/^.*?_/)[1] 

Primeiro força a partida a começar do início com o '^'. Em seguida, ele corresponde a qualquer número de caracteres que não sejam '_', ou seja, todos os caracteres antes do primeiro '_'.

O '?' significa que um número mínimo de caracteres que fazem com que todo o padrão corresponda é correspondido pelo '. *?' porque é seguido por '_', que é incluído na partida como seu último caractere.

Portanto, este split () usa uma peça correspondente como seu 'divisor' e a remove dos resultados. Por isso, remove tudo até e incluindo o primeiro '_' e fornece o restante como o segundo elemento do resultado. O primeiro elemento é "" representando a peça antes da peça correspondente. É "" porque a partida começa do começo.

Existem outros RegExps que funcionam tão bem como /_(.*)/ fornecidos por Chandu em uma resposta anterior.

O /^.*?_/ tem o benefício de que você pode entender o que ele faz sem precisar saber sobre o papel especial que os grupos de captura desempenham com replace ().

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.