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.


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

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)


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ę!


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

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

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


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

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.


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

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


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.


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))


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(/[\/\\]/);

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>


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

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


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


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

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>

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

1
To nie robi nic z palindromami , tylko słowami.
Nathan Tuggy,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.