dividere la stringa solo sulla prima istanza del carattere specificato


272

Nel mio codice ho diviso una stringa in base _e ho afferrato il secondo elemento dell'array.

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

Prende good_lucke mi fornisce luck. Funziona alla grande!

Ma ora ho una classe che sembra good_luck_buddy. Come faccio a far sì che il mio javascript ignori il secondo _e mi dia luck_buddy?

L'ho trovato var field = element.split(new char [] {'_'}, 2);nella risposta ac # stackoverflow ma non funziona. L'ho provato su jsFiddle ...

Risposte:


408

Usa le parentesi di cattura :

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

Sono definiti come

Se separatorcontiene parentesi di acquisizione, i risultati corrispondenti vengono restituiti nella matrice.

Quindi in questo caso vogliamo dividere in _.+(cioè dividere il separatore essendo una sottostringa che inizia con _) ma anche lasciare che il risultato contenga una parte del nostro separatore (cioè tutto dopo _).

In questo esempio il nostro separatore (corrispondenza _(.+)) è _luck_buddye il gruppo acquisito (all'interno del separatore) è lucky_buddy. Senza la parentesi di cattura, la luck_buddy(corrispondenza .+) non sarebbe stata inclusa nella matrice dei risultati, come nel caso dei semplici splitseparatori non sono inclusi nel risultato.


21
Non hai nemmeno bisogno di (?), Basta usare /_(.+)/ per catturare 1 carattere in più dopo il primo _
Mark

3
Molto elegante. Funziona come un fascino. Grazie.
Ofeargall,

12
Giusto per essere chiari, il motivo per cui questa soluzione funziona è perché tutto ciò che segue il primo _viene abbinato all'interno di un gruppo di acquisizione e viene aggiunto all'elenco di token per tale motivo.
Alan Moore,

28
Qualcuno sa perché ottengo un elemento stringa extra vuoto con questo: in: "Aspect Ratio: 16:9".split(/:(.+)/)out:["Aspect Ratio", " 16:9", ""]
katy lavallee

4
@katylavallee - Questo potrebbe aiutare: stackoverflow.com/questions/12836062/… Poiché il separatore è ": 16:9", non c'è nulla dopo il separatore, creando così la stringa vuota alla fine.
Derek 功夫 會 功夫

232

Per cosa hai bisogno di espressioni regolari e array?

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 fa distinzione tra maiuscole e minuscole.
Kennebec,

3
penso che questa sia la risposta migliore. è anche possibile ottenere una stringa dopo l' _myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 )
altra

9
La risposta genera la seconda parte della stringa. E se volessi anche la prima parte? Con var str = "good_luck_buddy", res = str.split(/_(.+)/);voi ottenere tutte le parti:console.log(res[0]); console.log(res[1]);
Sun

1
@PeterLeger let split = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ]Eccolo qui. Anche con il supporto dell'ago variabile
Steffan,

Questo è Genius!
stuckedoverflow

36

Evito RegExp a tutti i costi. Ecco un'altra cosa che puoi fare:

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

18
Chi ha paura di RegExp non potrà mai dire quanto sia grande RegExp. Devi trovare la porta da solo. Una volta che sei lì, non guarderai mai indietro. Chiedimi di nuovo tra qualche anno e mi dirai quanto è bello.
Christiaan Westerbeek,

3
@yonas Prendi la pillola rossa!
da

2
@yonas Sì, prendi la pillola rossa! Ti
Julian F. Weinert,

15
Ha! Ho scritto questo commento 4+ anni fa. Ora sono decisamente d'accordo con RegExp! :)
yonas,

3
@ion come faresti meglio a non farlo. RegExp è fantastico quando ne hai bisogno . Non è il caso qui. Controlla il test aggiornato: jsperf.com/split-by-first-colon/2
metalim

11

Sostituisci la prima istanza con un segnaposto univoco, quindi dividi da lì.

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

["good","luck_buddy"]

Ciò è più utile quando sono necessari entrambi i lati della divisione.


3
Ciò pone un vincolo non necessario sulla stringa.
Yan Foto

Questa risposta ha funzionato per me quando tutte le risposte sopra non lo hanno fatto.
GuitarViking,

1
@YanFoto intendi usando '&'? Potrebbe essere qualsiasi cosa.
sebjwallace,

2
@sebjwallace Qualunque cosa tu scelga, significa che non puoi avere quel personaggio nella stringa. Ad esempio "fish & chips_are_great" dà [fish, chips, are_great] penso.
Joe,

@Joe Potresti usare qualsiasi cosa invece di '&' - era solo un esempio. Se lo desideri, puoi sostituire la prima occorrenza di _ con ¬. Quindi "fish & chips_are_great" sostituirebbe la prima occorrenza di _ con ¬ per dare "fish & chips¬are_great", quindi diviso per ¬ per ottenere ["fish & chips", "are_great"]
sebjwallace,

8

Puoi usare l'espressione regolare come:

var arr = element.split(/_(.*)/)
È possibile utilizzare il secondo parametro che specifica il limite della divisione. ovvero: var field = element.split ('_', 1) [1];

6
Ciò specifica solo quante voci di divisione vengono restituite, non quante volte si divide. 'good_luck_buddy'.split('_', 1);ritorna solo['good']
Alex Vidal

Grazie fatto un presupposto al riguardo. Aggiornato il post per usare un'espressione regolare.
Chandu,

È stato (:?.*)doveva essere un gruppo non-cattura? In tal caso, dovrebbe essere (?:.*), ma se lo correggi scoprirai che non funziona più. (:?.*)corrisponde a un facoltativo :seguito da zero o più di qualsiasi carattere. Questa soluzione finisce per funzionare per lo stesso motivo per cui fa @ MarkF: tutto ciò che segue il primo _viene aggiunto all'elenco dei token perché è stato abbinato in un gruppo di acquisizione. (Inoltre, il gmodificatore non ha alcun effetto se usato in una regex divisa.)
Alan Moore,

Grazie, non me ne sono reso conto. Aggiornato il Regex e provato su un paio di scenari ...
Chandu,

1
Non funziona in ie8 e torno a indexOf e sottostringa
Igor Alekseev

6

Questa soluzione ha funzionato per me

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"

O

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
Questo non funziona se lo splitter ha più di 1 carattere, comunque.
Hayakam,

5

Al giorno d'oggi String.prototype.splitti permette davvero di limitare il numero di divisioni.

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

...

limite Facoltativo

Un numero intero non negativo che limita il numero di divisioni. Se fornito, divide la stringa ad ogni occorrenza del separatore specificato, ma si interrompe quando sono state inserite voci di limite nell'array. Qualsiasi testo residuo non è affatto incluso nell'array.

L'array può contenere meno voci del limite se viene raggiunta la fine della stringa prima del raggiungimento del limite. Se il limite è 0, non viene eseguita alcuna divisione.

avvertimento

Potrebbe non funzionare come previsto. Speravo che avrebbe semplicemente ignorato il resto dei delimitatori, ma invece, quando raggiunge il limite, divide nuovamente la stringa rimanente, omettendo la parte dopo la divisione dai risultati di ritorno.

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"]

Speravo in:

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"]

Anch'io. Sembra che PHP si stia dividendo in "primo" e "resto".
Banana Acid

4

String.splitPurtroppo , Javascript non ha modo di limitare il numero effettivo di divisioni. Ha un secondo argomento che specifica quanti degli articoli divisi effettivi vengono restituiti, il che non è utile nel tuo caso. La soluzione sarebbe quella di dividere la stringa, spostare il primo elemento fuori, quindi ricongiungersi agli altri elementi ::

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

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

Vedo che la funzione split non aiuta, ma l'uso di una regex sembra raggiungere questo obiettivo. Dovrebbe specificare che ti stai riferendo alla stessa funzione Split, in modo nativo.
Dan Hanly,

1
Interessante, questa soluzione distilla il problema in una soluzione più leggibile / gestibile. Nel mio caso di convertire un nome completo in primo e ultimo (sì, i nostri requisiti hanno costretto questa logica) questa soluzione ha funzionato meglio ed era più leggibile rispetto alle altre. Grazie
Sukima il

Questo non è più vero :)
Kraken,

3

Ho bisogno delle due parti di stringa, quindi, regex lookbehind mi aiuta in questo.

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


3

Con l'aiuto dell'assegnazione destrutturante può essere più leggibile:

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

2

Soluzione più veloce?

Ho eseguito alcuni benchmark e questa soluzione ha vinto enormemente: 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);
};

Confronto delle prestazioni con altre soluzioni

L'unico contendente stretto era la stessa riga di codice, tranne usare substrinvece di slice.

Altre soluzioni che ho provato che coinvolgono splito RegExps ha avuto un grande calo di prestazioni e sono stati circa 2 ordini di grandezza più lento. Utilizzando joinsui risultati di split, ovviamente, si aggiunge una penalità aggiuntiva per le prestazioni.

Perché sono più lenti? Ogni volta che deve essere creato un nuovo oggetto o array, JS deve richiedere un pezzo di memoria dal sistema operativo. Questo processo è molto lento.

Ecco alcune linee guida generali, nel caso tu stia inseguendo benchmark:

  • Le nuove allocazioni di memoria dinamica per oggetti {}o matrici [](come quella che splitcrea) costeranno molto in termini di prestazioni.
  • RegExp le ricerche sono più complicate e quindi più lente delle ricerche di stringhe.
  • Se disponi già di un array, gli array destrutturanti sono veloci quanto indicizzarli esplicitamente e sembrano fantastici.

Rimozione oltre la prima istanza

Ecco una soluzione che si suddividerà e includerà l'ennesima istanza. Non è altrettanto veloce, ma sulla domanda del PO, gobble(element, '_', 1)è ancora> 2x più veloce di una RegExpo splitsoluzione e può fare di più:

/*
`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);
}

Con la definizione sopra, gobble('path/to/file.txt', '/')darebbe il nome del file e gobble('prefix_category_item', '_', 1)rimuoverà il prefisso come la prima soluzione in questa risposta.


  1. I test sono stati eseguiti in Chrome 70.0.3538.110 su macOSX 10.14.

Andiamo ... È il 2019 ... Le persone là fuori stanno davvero ancora marcando microbenching su questo genere di cose?
Victor Schröder,

Sono d'accordo. Sebbene il microbenchmarking sia leggermente interessante, dovresti fare affidamento su un compilatore o un traduttore per le ottimizzazioni. Mb qualcuno che legge questo sta costruendo un compilatore o usando ejs / embedded e non può usare regex. Tuttavia, questo sembra più bello per il mio caso specifico rispetto a una regex. (Rimuoverei la "soluzione più veloce")
TamusJRoyce l'

1

La soluzione di Mark F è fantastica ma non è supportata dai vecchi browser. La soluzione di Kennebec è fantastica e supportata da vecchi browser ma non supporta regex.

Quindi, se stai cercando una soluzione che divide la stringa una sola volta, supportata da vecchi browser e supporta regex, ecco la mia soluzione:

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

Per i principianti come me che non sono abituati all'espressione regolare, questa soluzione alternativa ha funzionato:

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

Il metodo slice () estrae una parte di una stringa e restituisce una nuova stringa e il metodo indexOf () restituisce la posizione della prima occorrenza trovata di un valore specificato in una stringa.


Questa non è una soluzione alternativa, ma un modo corretto di farlo;)
Victor Schröder

1

Usa il replace()metodo string con un regex :

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

Questa regex corrisponde a 0 o più caratteri prima del primo _e dello _stesso. La corrispondenza viene quindi sostituita da una stringa vuota.


La document.body.innerHTMLparte qui è completamente inutile.
Victor Schröder,

@ VictorSchröder come ti aspetti di vedere l'output dello snippet senza document.body.innerHTML?
James T

2
document.bodydipende dalla presenza del DOM e non funzionerà su un ambiente JavaScript puro. console.logè sufficiente per questo scopo o semplicemente lasciare il risultato in una variabile per l'ispezione.
Victor Schröder,

@ VictorSchröder Non credo che avrebbe causato molta confusione, ma ho comunque modificato.
James T,

0

Questo ha funzionato per me su Chrome + FF:

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

Se hai bisogno anche della chiave prova questo:

"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

Ecco un RegExp che fa il trucco.

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

Innanzitutto forza la partita a partire dall'inizio con '^'. Quindi corrisponde a qualsiasi numero di caratteri che non sono '_', in altre parole tutti i caratteri prima del primo '_'.

Il '?' significa che un numero minimo di caratteri che fanno corrispondere l'intero modello sono abbinati al '. *?' perché è seguito da '_', che viene quindi incluso nella partita come ultimo carattere.

Pertanto questo split () usa una parte corrispondente come il suo 'splitter' e lo rimuove dai risultati. Quindi rimuove tutto fino al primo "_" incluso e ti dà il resto come secondo elemento del risultato. Il primo elemento è "" che rappresenta la parte prima della parte abbinata. È "" perché la partita inizia dall'inizio.

Ci sono altri RegExps che funzionano come /_(.*)/ dati da Chandu in una risposta precedente.

Il /^.*?_/ ha il vantaggio di poter capire cosa fa senza dover conoscere il ruolo speciale che i gruppi di acquisizione svolgono con Replace ().

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.