Teilungszeichenfolge nur bei der ersten Instanz des angegebenen Zeichens


271

In meinem Code habe ich eine Zeichenfolge basierend auf geteilt _und das zweite Element im Array abgerufen .

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

Nimmt good_luckund versorgt mich mit luck. Funktioniert super!

Aber jetzt habe ich eine Klasse, die aussieht good_luck_buddy. Wie kann ich mein Javascript dazu bringen, das zweite zu ignorieren _und mir zu geben luck_buddy?

Ich habe dies var field = element.split(new char [] {'_'}, 2);in der Antwort ac # stackoverflow gefunden, aber es funktioniert nicht. Ich habe es bei jsFiddle ausprobiert ...

Antworten:


406

Verwenden Sie Erfassungsklammern :

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

Sie sind definiert als

Wenn separatorErfassungsklammern enthalten sind, werden übereinstimmende Ergebnisse im Array zurückgegeben.

In diesem Fall möchten wir _.+also bei teilen (dh das Trennzeichen ist eine Teilzeichenfolge, die mit beginnt _), aber das Ergebnis soll auch einen Teil unseres Trennzeichens enthalten (dh alles danach _).

In diesem Beispiel ist unser Trennzeichen (Matching _(.+)) _luck_buddyund die erfasste Gruppe (innerhalb des Trennzeichens) lucky_buddy. Ohne die Erfassungsklammer wäre das luck_buddy(Übereinstimmende .+) nicht in das Ergebnisarray aufgenommen worden, da es einfach ist, splitdass Trennzeichen nicht im Ergebnis enthalten sind.


21
Sie brauchen nicht einmal (?), Verwenden Sie einfach /_(.+)/, um 1 weitere Zeichen nach dem ersten _
Mark

3
Sehr elegant. Klappt wunderbar. Danke dir.
Ofeargall

12
Der Grund, warum diese Lösung funktioniert, ist, dass alles, was nach dem ersten _übereinstimmt, innerhalb einer Erfassungsgruppe abgeglichen wird und aus diesem Grund zur Token-Liste hinzugefügt wird.
Alan Moore

28
Jeder weiß, warum ich damit ein extra leeres String-Element bekomme: in: "Aspect Ratio: 16:9".split(/:(.+)/)out:["Aspect Ratio", " 16:9", ""]
katy lavallee

4
@katylavallee - Dies könnte helfen: stackoverflow.com/questions/12836062/… Da sich das Trennzeichen befindet ": 16:9", befindet sich nach dem Trennzeichen nichts mehr, sodass am Ende die leere Zeichenfolge erstellt wird.
Derek 21 會 功夫

230

Wofür benötigen Sie reguläre Ausdrücke und Arrays?

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. Bei Javascript wird zwischen Groß- und Kleinschreibung unterschieden.
kennebec

3
Ich denke, das ist die beste Antwort. Es ist auch möglich, einen String nach dem anderen zu bekommen, _indem man schreibt:myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 )
muratgozel

9
Die Antwort gibt den zweiten Teil der Zeichenfolge aus. Was ist, wenn Sie auch den ersten Teil wollen? Mit var str = "good_luck_buddy", res = str.split(/_(.+)/);bekommen Sie alle Teile:console.log(res[0]); console.log(res[1]);
So

1
@PeterLeger let split = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ]Da hast du es. Auch mit Unterstützung der variablen Nadel
Steffan

Das ist Genie!
Stuckedoverflow

36

Ich vermeide RegExp um jeden Preis. Folgendes können Sie noch tun:

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

18
Wer Angst vor RegExp hat, kann nie sagen, wie großartig RegExp ist. Sie müssen die Tür selbst finden. Sobald Sie dort sind, werden Sie nie mehr zurückblicken. Fragen Sie mich in ein paar Jahren noch einmal und Sie werden mir sagen, wie großartig es ist.
Christiaan Westerbeek

3
@yonas Nimm die rote Pille!
Freitag,

2
@yonas Ja, nimm die rote Pille! Es wird Ihr Leben schneller machen, auch für kurze Saiten: jsperf.com/split-by-first-colon
Julian F. Weinert

15
Ha! Ich habe diesen Kommentar vor mehr als 4 Jahren geschrieben. Ich bin jetzt definitiv mit RegExp an Bord! :)
yonas

2
@yonas du besser nicht. RegExp ist fantastisch, wenn Sie es brauchen . Nicht der Fall hier. Überprüfen Sie den aktualisierten Test: jsperf.com/split-by-first-colon/2
metalim

11

Ersetzen Sie die erste Instanz durch einen eindeutigen Platzhalter und teilen Sie sie von dort auf.

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

["good","luck_buddy"]

Dies ist nützlicher, wenn beide Seiten der Aufteilung benötigt werden.


2
Dies stellt eine unnötige Einschränkung für die Zeichenfolge dar.
Yan Foto

Diese Antwort hat bei mir funktioniert, wenn alle oben genannten Antworten nicht zutrafen.
GuitarViking

1
@YanFoto meinst du mit '&'? Es könnte alles sein.
Sebjwallace

2
@sebjwallace Was auch immer Sie wählen, es bedeutet, dass Sie dieses Zeichen nicht in der Zeichenfolge haben können. ZB "fish & chip_are_great" gibt [fish, chip, are_great], denke ich.
Joe

@ Joe Du könntest alles anstelle von '&' verwenden - es war nur ein Beispiel. Sie können das erste Vorkommen von _ durch ¬ ersetzen, wenn Sie möchten. "Fish & Chips_are_great" würde also das erste Vorkommen von _ durch ¬ ersetzen, um "fish & chip¬are_great" zu ergeben, und dann durch ¬ geteilt werden, um ["fish & chip", "are_great"] zu erhalten
sebjwallace

8

Sie können den regulären Ausdruck wie folgt verwenden:

var arr = element.split(/_(.*)/)
Sie können den zweiten Parameter verwenden, der die Grenze der Aufteilung angibt. dh: var field = element.split ('_', 1) [1];

6
Das gibt nur an, wie viele der geteilten Elemente zurückgegeben werden, nicht wie oft sie geteilt werden. 'good_luck_buddy'.split('_', 1);kehrt gerade zurück['good']
Alex Vidal

Dank ging davon aus. Der Beitrag wurde aktualisiert, um einen regulären Ausdruck zu verwenden.
Chandu

Sollte (:?.*)eine nicht erfassende Gruppe sein? Wenn ja, sollte es sein (?:.*), aber wenn Sie es korrigieren, werden Sie feststellen, dass es nicht mehr funktioniert. (:?.*)Entspricht einem optionalen :Zeichen, gefolgt von null oder mehr Zeichen. Diese Lösung funktioniert letztendlich aus dem gleichen Grund, aus dem @ MarkF's funktioniert: Alles nach dem ersten _wird zur Token-Liste hinzugefügt, da es in einer Erfassungsgruppe abgeglichen wurde. (Auch der gModifikator hat keine Wirkung, wenn er in einem geteilten
Alan Moore

Danke, habe es nicht bemerkt. Aktualisierte den Regex und versuchte es über ein paar Szenarien ...
Chandu

1
Es funktioniert nicht in IE8 und ich wechsle zurück zu IndexOf und Teilzeichenfolge
Igor Alekseev

5

Heutzutage String.prototype.splitkönnen Sie die Anzahl der Teilungen tatsächlich begrenzen.

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

...

limit Optional

Eine nicht negative Ganzzahl, die die Anzahl der Teilungen begrenzt. Wenn angegeben, wird die Zeichenfolge bei jedem Auftreten des angegebenen Trennzeichens aufgeteilt, stoppt jedoch, wenn Grenzwerteinträge in das Array eingefügt wurden. Übrig gebliebener Text ist überhaupt nicht im Array enthalten.

Das Array kann weniger Einträge als limit enthalten, wenn das Ende der Zeichenfolge erreicht ist, bevor das Limit erreicht ist. Wenn der Grenzwert 0 ist, wird keine Aufteilung durchgeführt.

Vorbehalt

Es funktioniert möglicherweise nicht so, wie Sie es erwarten. Ich hatte gehofft, dass der Rest der Trennzeichen einfach ignoriert wird. Wenn das Limit jedoch erreicht ist, wird die verbleibende Zeichenfolge erneut aufgeteilt, wobei der Teil nach der Aufteilung aus den Rückgabeergebnissen weggelassen wird.

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

Ich hatte gehofft auf:

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

Hier gilt das gleiche. Es scheint, als würde sich PHP in "first" und "rest" aufteilen.
BananaAcid

5

Diese Lösung hat bei mir funktioniert

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"

ODER

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"

Dies funktioniert jedoch nicht, wenn der Splitter mehr als 1 Zeichen hat.
Haykam

4

Javascript hat String.splitleider keine Möglichkeit, die tatsächliche Anzahl der Teilungen zu begrenzen. Es gibt ein zweites Argument, das angibt, wie viele der tatsächlich geteilten Elemente zurückgegeben werden, was in Ihrem Fall nicht hilfreich ist. Die Lösung wäre, die Zeichenfolge zu teilen, das erste Element zu verschieben und dann die verbleibenden Elemente wieder zu verbinden ::

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

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

Ich sehe, dass die Split-Funktion nicht hilft, aber die Verwendung eines regulären Ausdrucks scheint dies zu erreichen. Es sollte angegeben werden, dass Sie sich nativ auf die Split-Funktion selbst beziehen.
Dan Hanly

1
Interessant, diese Lösung destilliert das Problem auf eine besser lesbare / verwaltbare Lösung. In meinem Fall, einen vollständigen Namen in einen ersten und einen letzten umzuwandeln (ja, unsere Anforderungen haben diese Logik erzwungen), funktionierte diese Lösung am besten und war besser lesbar als die anderen. Danke
Sukima

Das stimmt nicht mehr :)
Kraken

3

Ich brauche die beiden Teile des Strings, also hilft mir Regex Lookbehind dabei.

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


3

Mit Hilfe der Destrukturierungszuordnung kann es besser lesbar sein:

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

2

Schnellste Lösung?

Ich habe einige Benchmarks durchgeführt , und diese Lösung hat enorm gewonnen: 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);
};

Leistungsvergleich mit anderen Lösungen

Der einzige enge Anwärter war dieselbe Codezeile, außer dass substranstelle von slice.

Andere Lösungen, die ich mit splitoder ohne RegExps versucht habe, hatten einen großen Leistungseinbruch und waren etwa 2 Größenordnungen langsamer. Die Verwendung joinder Ergebnisse von splitfügt natürlich eine zusätzliche Leistungsstrafe hinzu.

Warum sind sie langsamer? Jedes Mal, wenn ein neues Objekt oder Array erstellt werden muss, muss JS einen Speicherblock vom Betriebssystem anfordern. Dieser Prozess ist sehr langsam.

Hier sind einige allgemeine Richtlinien für den Fall, dass Sie Benchmarks verfolgen:

  • Neue dynamische Speicherzuordnungen für Objekte {}oder Arrays [](wie die, die spliterstellt werden) kosten viel Leistung.
  • RegExp Suchen sind komplizierter und daher langsamer als Zeichenfolgensuchen.
  • Wenn Sie bereits über ein Array verfügen, ist die Destrukturierung von Arrays ungefähr so ​​schnell wie die explizite Indizierung und sieht fantastisch aus.

Über die erste Instanz hinaus entfernen

Hier ist eine Lösung, die bis zur n-ten Instanz reicht. Es ist nicht ganz so schnell, aber auf die Frage des OP gobble(element, '_', 1)ist es immer noch> 2x schneller als eine RegExpoder splitLösung und kann mehr:

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

Mit der obigen Definition gobble('path/to/file.txt', '/')würde der Name der Datei angegeben und gobble('prefix_category_item', '_', 1)das Präfix wie bei der ersten Lösung in dieser Antwort entfernt.


  1. Die Tests wurden in Chrome 70.0.3538.110 unter macOSX 10.14 ausgeführt.

Komm schon ... Es ist 2019 ... Markieren die Leute da draußen wirklich noch so etwas mit Mikrobenchmarking?
Victor Schröder

Genau. Obwohl Microbenchmarking etwas interessant ist, sollten Sie sich für Optimierungen auf einen Compiler oder Übersetzer verlassen. Wer weiß. Mb jemand, der dies liest, baut einen Compiler oder verwendet ejs / embedded und kann Regex nicht verwenden. Dies sieht jedoch für meinen speziellen Fall besser aus als eine Regex. (Ich würde die "schnellste Lösung" entfernen)
TamusJRoyce

1

Die Lösung von Mark F ist fantastisch, wird aber von alten Browsern nicht unterstützt. Die Lösung von Kennebec ist fantastisch und wird von alten Browsern unterstützt, unterstützt jedoch keinen regulären Ausdruck.

Wenn Sie also nach einer Lösung suchen, die Ihre Zeichenfolge nur einmal aufteilt, die von alten Browsern unterstützt wird und Regex unterstützt, ist hier meine Lösung:

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

Für Anfänger wie mich, die nicht an regulären Ausdruck gewöhnt sind, hat diese Problemumgehungslösung funktioniert:

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

Die Methode Slice () extrahiert einen Teil einer Zeichenfolge und gibt eine neue Zeichenfolge zurück. Die Methode indexOf () gibt die Position des ersten gefundenen Auftretens eines angegebenen Werts in einer Zeichenfolge zurück.


Dies ist keine Problemumgehung, sondern eine angemessene Vorgehensweise;)
Victor Schröder

1

Verwenden Sie die Zeichenfolgenmethode replace()mit einem regulären Ausdruck :

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

Dieser reguläre Ausdruck entspricht 0 oder mehr Zeichen vor dem ersten _und dem _selbst. Die Übereinstimmung wird dann durch eine leere Zeichenfolge ersetzt.


Der document.body.innerHTMLTeil hier ist völlig nutzlos.
Victor Schröder

@ VictorSchröder Wie erwarten Sie die Ausgabe des Snippets ohne document.body.innerHTML?
James T

1
document.bodyhängt davon ab, ob das DOM vorhanden ist, und es funktioniert nicht in einer reinen JavaScript-Umgebung. console.logreicht für diesen Zweck aus oder belassen Sie das Ergebnis einfach in einer Variablen zur Überprüfung.
Victor Schröder

@ VictorSchröder Ich glaube nicht, dass es viel Verwirrung verursacht hätte, aber ich habe es trotzdem bearbeitet.
James T

0

Das hat bei Chrome + FF bei mir funktioniert:

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

Wenn Sie auch den Schlüssel benötigen, versuchen Sie Folgendes:

"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

Hier ist ein RegExp, das den Trick macht.

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

Zuerst wird das Match gezwungen, mit dem '^' von vorne zu beginnen. Dann stimmt es mit einer beliebigen Anzahl von Zeichen überein, die nicht '_' sind, dh alle Zeichen vor dem ersten '_'.

Das '?' bedeutet, dass eine minimale Anzahl von Zeichen, mit denen das gesamte Muster übereinstimmt, mit dem '. *?' weil es von '_' gefolgt wird, das dann als letztes Zeichen in das Match aufgenommen wird.

Daher verwendet dieses split () ein passendes Teil wie seinen 'Splitter' und entfernt es aus den Ergebnissen. Es entfernt also alles bis einschließlich des ersten '_' und gibt Ihnen den Rest als zweites Element des Ergebnisses. Das erste Element ist "", das das Teil vor dem übereinstimmenden Teil darstellt. Es ist "", weil das Match von vorne beginnt.

Es gibt andere RegExps, die genauso gut funktionieren wie /_(.*)/, die Chandu in einer früheren Antwort gegeben hat.

Das /^.*?_/ hat den Vorteil, dass Sie verstehen können, was es tut, ohne über die besondere Rolle Bescheid wissen zu müssen, die Erfassungsgruppen mit replace () spielen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.