Jak podzielić ciąg z wieloma separatorami w javascript?

504

Jak podzielić ciąg z wieloma separatorami w JavaScript? Próbuję dzielić na przecinki i spacje, ale AFAIK, funkcja podziału JS obsługuje tylko jeden separator.

mikemaccana
źródło
3
Miałem ten problem, próbując podzielić ścieżki plików, które zostały zbudowane przy pomocy nodejs pod Windows. Czasami pojawiały się ukośniki „/” i „\” wstecz na tej samej ścieżce.
Fuhrmanator

Odpowiedzi:

707

Przekaż wyrażenie regularne jako parametr:

js> "Hello awesome, world!".split(/[\s,]+/)
Hello,awesome,world!

Edytowano, aby dodać:

Możesz uzyskać ostatni element, wybierając długość tablicy minus 1:

>>> bits = "Hello awesome, world!".split(/[\s,]+/)
["Hello", "awesome", "world!"]
>>> bit = bits[bits.length - 1]
"world!"

... a jeśli wzór nie pasuje:

>>> bits = "Hello awesome, world!".split(/foo/)
["Hello awesome, world!"]
>>> bits[bits.length - 1]
"Hello awesome, world!"
Aaron Maenpaa
źródło
1
Czego używasz do swojej konsoli js>?
rdzeń
4
rhino, implementacja JavaScriptu w Javie przez Mozillę: mozilla.org/rhino (... lub „sudo apt-get install rhino”).
Aaron Maenpaa,
dzięki. Kolejnym pytaniem związanym z tym, co muszę zrobić, jest pobranie ostatniego elementu podzielonej tablicy. jeśli nie ma tablicy, powinien zwrócić ciąg thx
2
Czy jest jakiś sposób na uniknięcie usuwania separatorów podczas dzielenia wyrażeń regularnych?
Anderson Green
Jak rozdzielić zarówno ciąg „hello world”, jak i inną postać (lub inny regex), na przykład symbol potoku? Wypróbowane odmiany, (hello world)|\|które jeszcze nie do końca działały. Jakieś pomysły?
orzechowy o natty
183

Możesz przekazać wyrażenie regularne do operatora podziału JavaScript . Na przykład:

"1,2 3".split(/,| /) 
["1", "2", "3"]

Lub jeśli chcesz zezwolić, aby wiele separatorów działało jako jeden:

"1, 2, , 3".split(/(?:,| )+/) 
["1", "2", "3"]

(Musisz użyć nie przechwytujących parens (? :), ponieważ w przeciwnym razie zostanie ono ponownie połączone w wynik. Możesz też być mądry jak Aaron i użyć klasy postaci.)

(Przykłady przetestowane w Safari + FF)

Jesse Rusak
źródło
3
Jeśli potrzebujesz wielu znaków, aby działały jako jeden, np. Powiedz „one; #two; #new jersey”, możesz po prostu przekazać ciąg „; #” do funkcji podziału. "one; #two; #new jersey" .split ("; #") [2] === "nowa koszulka"
Oskar Austegard
Ta metoda działa lepiej niż klasy postaci, jeśli chcesz podzielić więcej niż jeden znak. Rozdziel je, |jak pokazuje Jesse.
devios1
Zastanawiam się, czy istnieje sposób na uniknięcie usuwania separatorów podczas dzielenia łańcucha za pomocą wyrażenia regularnego: ten przykład usuwa separatory, ale mam nadzieję, że możliwe jest podzielenie łańcucha bez usuwania ich.
Anderson Green
1
@AndersonGreen To zależy dokładnie od tego, czego chcesz; w tym przypadku istnieje wiele separatorów, więc czy chcesz je wszystkie zachować? Jako osobny przedmiot? Dołączyłeś do poprzedniego elementu? Następny przedmiot? Wydaje mi się niejasne. Możesz zadać nowe pytanie z przykładami tego, czego szukasz.
Jesse Rusak
@JesseRusak Miałem na myśli zachowanie wszystkich separatorów jako osobnych elementów, aby łańcuch mógł być tokenizowany za pomocą listy separatorów.
Anderson Green
55

Inną prostą, ale skuteczną metodą jest wielokrotne używanie funkcji split + join.

"a=b,c:d".split('=').join(',').split(':').join(',').split(',')

Zasadniczo dokonanie podziału, po którym następuje łączenie, jest jak zamiana globalna, więc zastępuje każdy separator przecinkiem, a po zastąpieniu wszystkich dokonuje ostatecznego podziału przecinkiem

Wynikiem powyższego wyrażenia jest:

['a', 'b', 'c', 'd']

Rozwijając to, możesz również umieścić go w funkcji:

function splitMulti(str, tokens){
        var tempChar = tokens[0]; // We can use the first token as a temporary join character
        for(var i = 1; i < tokens.length; i++){
            str = str.split(tokens[i]).join(tempChar);
        }
        str = str.split(tempChar);
        return str;
}

Stosowanie:

splitMulti('a=b,c:d', ['=', ',', ':']) // ["a", "b", "c", "d"]

Jeśli często korzystasz z tej funkcji, może warto rozważyć owijanie String.prototype.splitdla wygody (myślę, że moja funkcja jest dość bezpieczna - jedyne, co należy wziąć pod uwagę, to dodatkowe obciążenie warunków warunkowych (niewielkie) i fakt, że brakuje implementacji argumentu limitu jeśli tablica zostanie przekazana).

Pamiętaj, aby dołączyć tę splitMultifunkcję, jeśli używasz tego podejścia do poniższego opisu po prostu ją otacza :). Warto również zauważyć, że niektórzy ludzie marszczą brwi przy rozszerzaniu wbudowanych (ponieważ wiele osób robi to źle i mogą wystąpić konflikty), więc w razie wątpliwości porozmawiaj z kimś starszym przed użyciem tego lub zapytaj na SO :)

    var splitOrig = String.prototype.split; // Maintain a reference to inbuilt fn
    String.prototype.split = function (){
        if(arguments[0].length > 0){
            if(Object.prototype.toString.call(arguments[0]) == "[object Array]" ) { // Check if our separator is an array
                return splitMulti(this, arguments[0]);  // Call splitMulti
            }
        }
        return splitOrig.apply(this, arguments); // Call original split maintaining context
    };

Stosowanie:

var a = "a=b,c:d";
    a.split(['=', ',', ':']); // ["a", "b", "c", "d"]

// Test to check that the built-in split still works (although our wrapper wouldn't work if it didn't as it depends on it :P)
        a.split('='); // ["a", "b,c:d"] 

Cieszyć się!

Brian
źródło
3
Dlaczego piszesz, for(var i = 0; i < tokens.length; i++)a nie for(var i = 1; i < tokens.length; i++)?
tic
Przegapiłem tę optymalizację, masz rację, możemy zacząć tokens[1]od zapisania jednej iteracji, tokens[0] == tempchara tempcharpo tokenszakończeniu iteracji podzieliliśmy się, aby zakończyć. Zaktualizuję odpowiedź odpowiednio dzięki @tic :).
Brian
20

Uprośćmy: (dodanie „[] +” do RegEx oznacza „1 lub więcej”)

Oznacza to, że „+” i „{1,}” są takie same.

var words = text.split(/[ .:;?!~,`"&|()<>{}\[\]\r\n/\\]+/); // note ' and - are kept
Asher
źródło
2
dodać „+” na końcu oznacza 1 lub więcej
Asher
6
Powiedziałbym, że to minimalne, a nie proste
Darryl Hebbes
Dla + i - :-D, ale także \ s zamiast pustego znaku: var words = text.split (/ [\ s.:;?!~,`"&|()<>{}\= \ + \ - [] \ r \ n / \] + /);
Didier68
12

Podstępna metoda:

var s = "dasdnk asd, (naks) :d skldma";
var a = s.replace('(',' ').replace(')',' ').replace(',',' ').split(' ');
console.log(a);//["dasdnk", "asd", "naks", ":d", "skldma"]

źródło
3
jest to błędne, ponieważ .replace () nie zastępuje wszystkich elementów:/
1
można zmienić '('za /(/gwymienić wszystkie (elementy - gjest globalna flaga dla RegExp - więc szukać wszystkich wystąpień (nie pierwsza
codename-
7

Dla tych z Was, którzy chcą większej personalizacji funkcji dzielenia, napisałem algorytm rekurencyjny, który dzieli dany ciąg z listą znaków do podziału. Napisałem to zanim zobaczyłem powyższy post. Mam nadzieję, że pomoże to niektórym sfrustrowanym programistom.

splitString = function(string, splitters) {
    var list = [string];
    for(var i=0, len=splitters.length; i<len; i++) {
        traverseList(list, splitters[i], 0);
    }
    return flatten(list);
}

traverseList = function(list, splitter, index) {
    if(list[index]) {
        if((list.constructor !== String) && (list[index].constructor === String))
            (list[index] != list[index].split(splitter)) ? list[index] = list[index].split(splitter) : null;
        (list[index].constructor === Array) ? traverseList(list[index], splitter, 0) : null;
        (list.constructor === Array) ? traverseList(list, splitter, index+1) : null;    
    }
}

flatten = function(arr) {
    return arr.reduce(function(acc, val) {
        return acc.concat(val.constructor === Array ? flatten(val) : val);
    },[]);
}

var stringToSplit = "people and_other/things";
var splitList = [" ", "_", "/"];
splitString(stringToSplit, splitList);

Powyższy przykład zwraca: ["people", "and", "other", "things"]

Uwaga: flattenfunkcja została zaczerpnięta z kodu Rosetta

Stephen Sweriduk
źródło
6

Możesz po prostu połączyć wszystkie znaki, których chcesz użyć jako separatory, pojedynczo lub zbiorowo, w wyrażenie regularne i przekazać je do funkcji podziału. Na przykład możesz napisać:

console.log( "dasdnk asd, (naks) :d skldma".split(/[ \(,\)]+/) );

Wyjście będzie:

["dasdnk", "asd", "naks", ":d", "skldma"]
PeterKA
źródło
3

Być może powinieneś zrobić coś w rodzaju zamiany łańcucha, aby zamienić jeden separator na drugi, tak abyś miał tylko jeden separator, który poradziłby sobie w podziale.

TheTXI
źródło
3

Cześć, na przykład, jeśli podzieliłeś i zastąpiłeś w Ciąg 07:05:45

var hour = time.replace("PM", "").split(":");

Wynik

[ '07', '05', '45' ]
Ezequiel García
źródło
3

Oto nowy sposób na osiągnięcie tego samego w ES6 :

function SplitByString(source, splitBy) {
  var splitter = splitBy.split('');
  splitter.push([source]); //Push initial value

  return splitter.reduceRight(function(accumulator, curValue) {
    var k = [];
    accumulator.forEach(v => k = [...k, ...v.split(curValue)]);
    return k;
  });
}

var source = "abc,def#hijk*lmn,opq#rst*uvw,xyz";
var splitBy = ",*#";
console.log(SplitByString(source, splitBy));

Uwaga w tej funkcji:

  • Nie dotyczy to Regex
  • Zwraca podzieloną wartość w tej samej kolejności, w jakiej występuje source

Wynikiem powyższego kodu będzie:

wprowadź opis zdjęcia tutaj

Wisznu
źródło
2
a = "a=b,c:d"

array = ['=',',',':'];

for(i=0; i< array.length; i++){ a= a.split(array[i]).join(); }

to zwróci ciąg bez specjalnego cewnika.

gaurav krishna
źródło
2

Mój refaktor @Brian odpowiedzi

var string = 'and this is some kind of information and another text and simple and some egample or red or text';
var separators = ['and', 'or'];

function splitMulti(str, separators){
            var tempChar = 't3mp'; //prevent short text separator in split down
            
            //split by regex e.g. \b(or|and)\b
            var re = new RegExp('\\b(' + separators.join('|') + ')\\b' , "g");
            str = str.replace(re, tempChar).split(tempChar);
            
            // trim & remove empty
            return str.map(el => el.trim()).filter(el => el.length > 0);
}

console.log(splitMulti(string, separators))

JanuszO
źródło
1

Uważam, że jednym z głównych powodów, dla których potrzebuję tego, jest podzielenie ścieżek plików zarówno na, jak /i na \. To trochę trudne wyrażenie, więc opublikuję to tutaj w celach informacyjnych:

var splitFilePath = filePath.split(/[\/\\]/);
AlliterativeAlice
źródło
1

Myślę, że łatwiej jest określić, co chcesz zostawić, niż to, co chcesz usunąć.

Jeśli chcesz mieć tylko angielskie słowa, możesz użyć czegoś takiego:

text.match(/[a-z'\-]+/gi);

Przykłady (fragment kodu):

var R=[/[a-z'\-]+/gi,/[a-z'\-\s]+/gi];
var s=document.getElementById('s');
for(var i=0;i<R.length;i++)
 {
  var o=document.createElement('option');
  o.innerText=R[i]+'';
  o.value=i;
  s.appendChild(o);
 }
var t=document.getElementById('t');
var r=document.getElementById('r');

s.onchange=function()
 {
  r.innerHTML='';
  var x=s.value;
  if((x>=0)&&(x<R.length))
   x=t.value.match(R[x]);
  for(i=0;i<x.length;i++)
   {
    var li=document.createElement('li');
    li.innerText=x[i];
    r.appendChild(li);
   }
 }
<textarea id="t" style="width:70%;height:12em">even, test; spider-man

But saying o'er what I have said before:
My child is yet a stranger in the world;
She hath not seen the change of fourteen years,
Let two more summers wither in their pride,
Ere we may think her ripe to be a bride.

—Shakespeare, William. The Tragedy of Romeo and Juliet</textarea>

<p><select id="s">
 <option selected>Select a regular expression</option>
 <!-- option value="1">/[a-z'\-]+/gi</option>
 <option value="2">/[a-z'\-\s]+/gi</option -->
</select></p>
 <ol id="r" style="display:block;width:auto;border:1px inner;overflow:scroll;height:8em;max-height:10em;"></ol>
</div>

ESL
źródło
1

Począwszy od rozwiązania @ stephen-sweriduk (to było dla mnie bardziej interesujące!), Nieco zmodyfikowałem go, aby stał się bardziej ogólny i wielokrotnego użytku:

/**
 * Adapted from: http://stackoverflow.com/questions/650022/how-do-i-split-a-string-with-multiple-separators-in-javascript
*/
var StringUtils = {

  /**
   * Flatten a list of strings
   * http://rosettacode.org/wiki/Flatten_a_list
   */
  flatten : function(arr) {
    var self=this;
    return arr.reduce(function(acc, val) {
        return acc.concat(val.constructor === Array ? self.flatten(val) : val);
    },[]);
  },

  /**
   * Recursively Traverse a list and apply a function to each item
   * @param list array
   * @param expression Expression to use in func
   * @param func function of (item,expression) to apply expression to item
   *
   */
  traverseListFunc : function(list, expression, index, func) {
    var self=this;
    if(list[index]) {
        if((list.constructor !== String) && (list[index].constructor === String))
            (list[index] != func(list[index], expression)) ? list[index] = func(list[index], expression) : null;
        (list[index].constructor === Array) ? self.traverseListFunc(list[index], expression, 0, func) : null;
        (list.constructor === Array) ? self.traverseListFunc(list, expression, index+1, func) : null;
    }
  },

  /**
   * Recursively map function to string
   * @param string
   * @param expression Expression to apply to func
   * @param function of (item, expressions[i])
   */
  mapFuncToString : function(string, expressions, func) {
    var self=this;
    var list = [string];
    for(var i=0, len=expressions.length; i<len; i++) {
        self.traverseListFunc(list, expressions[i], 0, func);
    }
    return self.flatten(list);
  },

  /**
   * Split a string
   * @param splitters Array of characters to apply the split
   */
  splitString : function(string, splitters) {
    return this.mapFuncToString(string, splitters, function(item, expression) {
      return item.split(expression);
    })
  },

}

i wtedy

var stringToSplit = "people and_other/things";
var splitList = [" ", "_", "/"];
var splittedString=StringUtils.splitString(stringToSplit, splitList);
console.log(splitList, stringToSplit, splittedString);

który zwraca jako oryginał:

[ ' ', '_', '/' ] 'people and_other/things' [ 'people', 'and', 'other', 'things' ]
loretoparisi
źródło
1

Łatwym sposobem na to jest przetworzenie każdego znaku ciągu za pomocą każdego separatora i zbudowanie tablicy podziałów:

splix = function ()
{
  u = [].slice.call(arguments); v = u.slice(1); u = u[0]; w = [u]; x = 0;

  for (i = 0; i < u.length; ++i)
  {
    for (j = 0; j < v.length; ++j)
    {
      if (u.slice(i, i + v[j].length) == v[j])
      {
        y = w[x].split(v[j]); w[x] = y[0]; w[++x] = y[1];
      };
    };
  };

  return w;
};

Stosowanie: splix(string, delimiters...)

Przykład: splix("1.23--4", ".", "--")

Zwroty: ["1", "23", "4"]

Harr-Will
źródło
1

Zapewnię klasyczną implementację takiej funkcji. Kod działa w prawie wszystkich wersjach JavaScript i jest w pewien sposób optymalny.

  • Nie używa wyrażenia regularnego, co jest trudne do utrzymania
  • Nie wykorzystuje nowych funkcji JavaScript
  • Nie używa wielu wywołań .split () .join (), które wymagają więcej pamięci komputera

Po prostu czysty kod:

var text = "Create a function, that will return an array (of string), with the words inside the text";

println(getWords(text));

function getWords(text)
{
    let startWord = -1;
    let ar = [];

    for(let i = 0; i <= text.length; i++)
    {
        let c = i < text.length ? text[i] : " ";

        if (!isSeparator(c) && startWord < 0)
        {
            startWord = i;
        }

        if (isSeparator(c) && startWord >= 0)
        {
            let word = text.substring(startWord, i);
            ar.push(word);

            startWord = -1;
        }
    }

    return ar;
}

function isSeparator(c)
{
    var separators = [" ", "\t", "\n", "\r", ",", ";", ".", "!", "?", "(", ")"];
    return separators.includes(c);
}

Możesz zobaczyć kod działający na placu zabaw: https://codeguppy.com/code.html?IJI0E4OGnkyTZnoszAzf

codeguppy
źródło
0

Nie znam wydajności RegEx, ale tutaj jest kolejna alternatywa dla RegEx wykorzystuje natywny HashSet i działa zamiast tego w złożoności O (max (str. Długość, delimeter.length)):

var multiSplit = function(str,delimiter){
    if (!(delimiter instanceof Array))
        return str.split(delimiter);
    if (!delimiter || delimiter.length == 0)
        return [str];
    var hashSet = new Set(delimiter);
    if (hashSet.has(""))
        return str.split("");
    var lastIndex = 0;
    var result = [];
    for(var i = 0;i<str.length;i++){
        if (hashSet.has(str[i])){
            result.push(str.substring(lastIndex,i));
            lastIndex = i+1;
        }
    }
    result.push(str.substring(lastIndex));
    return result;
}

multiSplit('1,2,3.4.5.6 7 8 9',[',','.',' ']);
// Output: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]

multiSplit('1,2,3.4.5.6 7 8 9',' ');
// Output: ["1,2,3.4.5.6", "7", "8", "9"]
Orhun Alp Oral
źródło
11
Tak, a może przetestujesz coś, co piszesz? jsperf.com/slice-vs-custom To pokazuje, że twój kod jest w tym przykładzie 10 razy wolniejszy. Co podsunęło Ci pomysł, że stosowanie 2-krotnego wycinania, 2-krotnego konkatowania, 1-krotnego podziału, 1 przesunięcia czasowego i braku buforowania długości jest przyjazne dla wydajności?
Petar,
Zaktualizowałem kod, teraz jest tylko minimalna ilość kromki bez przesunięcia, podziału itp.
Orhun Alp Oral
0

Nie najlepszy sposób, ale działa w przypadku podziału z wieloma separatorami / separatorami

HTML

<button onclick="myFunction()">Split with Multiple and Different seperators/delimiters</button>
<p id="demo"></p>

javascript

<script>
function myFunction() {

var str = "How : are | you doing : today?";
var res = str.split(' | ');

var str2 = '';
var i;
for (i = 0; i < res.length; i++) { 
    str2 += res[i];

    if (i != res.length-1) {
      str2 += ",";
    }
}
var res2 = str2.split(' : ');

//you can add countless options (with or without space)

document.getElementById("demo").innerHTML = res2;
</script>
Stavros
źródło
-3

Korzystam z wyrażenia regularnego:

str =  'Write a program that extracts from a given text all palindromes, e.g. "ABBA", "lamal", "exe".';

var strNew = str.match(/\w+/g);

// Output: ["Write", "a", "program", "that", "extracts", "from", "a", "given", "text", "all", "palindromes", "e", "g", "ABBA", "lamal", "exe"]
Dodi Iwanow
źródło
1
To nie robi nic z palindromami , tylko słowami.
Nathan Tuggy,