разделить строку только в первом экземпляре указанного символа

272

В моем коде я разбил строку на основе _и получил второй элемент в массиве.

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

Берет good_luckи предоставляет мне luck. Прекрасно работает!

Но теперь у меня есть класс, который выглядит так good_luck_buddy. Как мне заставить мой javascript игнорировать второе _и дать мне luck_buddy?

Я нашел это var field = element.split(new char [] {'_'}, 2);в ответе ac # stackoverflow, но он не работает. Я попробовал это в jsFiddle ...

Ofeargall
источник

Ответы:

408

Используйте захватывающие скобки :

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

Они определены как

Если separatorсодержит круглые скобки, соответствующие результаты возвращаются в массив.

Таким образом, в этом случае мы хотим разделить в _.+(то есть разделитель разделения, являющийся подстрокой, начинающейся с _), но также позволить результату содержать некоторую часть нашего разделителя (то есть все после _).

В этом примере наш разделитель (совпадающий _(.+)) - это _luck_buddyи захваченная группа (внутри разделителя) lucky_buddy. Без круглых скобок luck_buddy(сопоставление .+) не было бы включено в массив результатов, как в случае с простым, splitчто разделители не включаются в результат.

отметка
источник
21
Вам даже не нужно (?), Просто используйте /_(.+)/ для захвата еще 1 символа после первого _
Марк
3
Очень элегантно. Работает как шарм. Спасибо.
Ofeargall
12
Просто чтобы прояснить, причина, по которой это решение работает, состоит в том, что все после первого _сопоставляется внутри группы захвата и по этой причине добавляется в список токенов.
Алан Мур
28
Кто-нибудь знает, почему я получаю дополнительный пустой строковый элемент с этим: in: "Aspect Ratio: 16:9".split(/:(.+)/)out:["Aspect Ratio", " 16:9", ""]
katy lavallee
4
@katylavallee - это может помочь: stackoverflow.com/questions/12836062/… Поскольку разделитель есть ": 16:9", после разделителя ничего нет, поэтому в конце создается пустая строка.
Дерек 朕 會 功夫
232

Для чего вам нужны регулярные выражения и массивы?

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

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

Kennebec
источник
5
строка! == строка Javascript чувствителен к регистру.
Кеннебек
3
Я думаю, что это лучший ответ. также можно получить строку после секунды _, написав:myString.substring( myString.indexOf('_', myString().indexOf('_') + 1) + 1 )
muratgozel
9
Ответ выводит вторую часть строки. Что, если вам тоже нужна первая часть? С var str = "good_luck_buddy", res = str.split(/_(.+)/);вами вы получите все запчасти:console.log(res[0]); console.log(res[1]);
Вс
1
@PeterLeger let split = [ string.substring(0, string.indexOf(options.divider)), string.substring(string.indexOf(options.divider) + 1) ]Вот и все. Также с поддержкой переменной иглы
Steffan
Это гений!
stuckedoverflow
36

Я избегаю RegExp любой ценой. Вот еще одна вещь, которую вы можете сделать:

"good_luck_buddy".split('_').slice(1).join('_')
Йонас
источник
18
Тот, кто боится RegExp, никогда не сможет сказать, насколько велик RegExp. Вы должны найти дверь самостоятельно. Оказавшись там, ты никогда не оглянешься назад. Спросите меня снова через несколько лет, и вы скажете мне, как это здорово.
Кристиан Вестербик
3
@yonas Возьми красную таблетку!
frnhr
2
@yonas Да, возьми красную таблетку! Это сделает вашу жизнь быстрее, даже для коротких строк: jsperf.com/split-by-first-colon
Джулиан Ф. Вейнерт,
15
Ха! Я написал этот комментарий 4+ лет назад. Я определенно нахожусь на борту с RegExp сейчас! :)
Йонас
3
@yonas тебе лучше этого не делать. RegExp потрясающий, когда вам это нужно . Здесь не тот случай. Проверьте обновленный тест: jsperf.com/split-by-first-colon/2
metalim
11

Замените первый экземпляр уникальным заполнителем, а затем разделите его.

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

["good","luck_buddy"]

Это более полезно, когда необходимы обе стороны разделения.

sebjwallace
источник
3
Это накладывает ненужные ограничения на строку.
Ян Фото
Этот ответ работал для меня, когда все вышеперечисленные ответы не.
GuitarViking
1
@YanFoto вы имеете в виду, используя '&'? Это может быть что угодно.
sebjwallace
2
@sebjwallace Что бы вы ни выбрали, это означает, что у вас не может быть этого символа в строке. Например, "fish & chips_are_great" дает [fish, chips, are_great], я думаю.
Джо
@Joe Вы можете использовать что угодно вместо '&' - это был просто пример. Вы можете заменить первое вхождение _ на ¬, если хотите. Таким образом, «fish & chips_are_great» заменит первое вхождение _ на ¬, чтобы дать «fish & chips¬are_great», а затем разделит на ¬, чтобы получить [«fish & chips», «are_great»]
sebjwallace
8

Вы можете использовать регулярное выражение как:

var arr = element.split(/_(.*)/)
Вы можете использовать второй параметр, который определяет предел разделения. то есть: var field = element.split ('_', 1) [1];
Chandu
источник
6
Это только указывает, сколько из разделенных элементов возвращается, а не сколько раз оно разделяется. 'good_luck_buddy'.split('_', 1);возвращается только['good']
Алекс Видаль
Спасибо сделал предположение об этом. Обновлен пост, чтобы использовать регулярное выражение.
Chandu
Был (:?.*)должен быть не захватив группу? Если так, то так и должно быть (?:.*), но если вы исправите это, вы обнаружите, что оно больше не работает. (:?.*)соответствует необязательному, :за которым следует ноль или более любого символа. Это решение в конечном итоге работает по той же причине, что и @ MarkF: все, что после первого _, добавляется в список токенов, потому что оно было найдено в группе захвата. (Кроме того, gмодификатор не имеет эффекта при использовании в регулярном выражении с разделением.)
Алан Мур
Спасибо, не поняла это. Обновил Regex и попробовал его на пару сценариев ...
Chandu
1
Это не работает в ie8, и я переключаюсь обратно на indexOf и подстроку
Игорь Алексеев
6

Это решение сработало для меня

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"

ИЛИ

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
Однако это не работает, если разделитель имеет более 1 символа.
Хайкам
5

В настоящее время String.prototype.splitдействительно позволяет ограничить количество расколов.

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

...

лимит необязательно

Неотрицательное целое число, ограничивающее количество разбиений. Если предусмотрено, разбивает строку при каждом появлении указанного разделителя, но останавливается, когда в массив помещаются предельные записи. Любой оставшийся текст вообще не включается в массив.

Массив может содержать меньше записей, чем ограничение, если конец строки достигнут до достижения предела. Если предел равен 0, расщепление не выполняется.

предостережение

Это может работать не так, как вы ожидаете. Я надеялся, что он просто проигнорирует остальные разделители, но вместо этого, когда он достигнет предела, он снова разбивает оставшуюся строку, пропуская часть после разделения в возвращаемых результатах.

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

Я надеялся на:

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"]
Kraken
источник
Тоже самое. Похоже, что PHP разделяется на «первое» и «остальное».
BananaAcid
4

К String.splitсожалению, в Javascript нет способа ограничить фактическое количество разделений. У него есть второй аргумент, который указывает, сколько фактических разделенных элементов возвращается, что бесполезно в вашем случае. Решением было бы разделить строку, сдвинуть первый элемент, а затем воссоединить оставшиеся элементы:

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

parts.shift(); // removes the first item from the array
var field = parts.join('_');
Алекс Видаль
источник
Я вижу, что функция split не помогает, но использование регулярных выражений, похоже, позволяет этого достичь. Следует указать, что вы имеете в виду саму функцию Split, изначально.
Дэн Ханли
1
Интересно, что это решение сводит проблему к более читаемому / управляемому решению. В моем случае преобразования полного имени в имя и фамилию (да, наши требования требовали такой логики), это решение работало лучше и было более читабельным, чем другие. Спасибо
Sukima
Это больше не так :)
Kraken
3

Мне нужны две части строки, так что, regex lookbehind поможет мне в этом.

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

Эдипо Коста Ребушас
источник
3

С помощью задания деструктуризации это может быть более читабельным:

let [first, ...rest] = "good_luck_buddy".split('_')
rest = rest.join('_')
ont.rif
источник
2

Самое быстрое решение?

Я провел несколько тестов , и это решение выиграло: 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);
};

Сравнение производительности с другими решениями

Единственным близким соперником была та же строка кода, за исключением использования substrвместо slice.

Другие решения, которые я пытался задействовать, splitили RegExps имели большой удар по производительности и были примерно на 2 порядка медленнее. Использование joinпо итогам split, конечно, добавляет дополнительную потерю производительности.

Почему они медленнее? Каждый раз, когда необходимо создать новый объект или массив, JS должен запросить кусок памяти у ОС. Этот процесс очень медленный.

Вот некоторые общие рекомендации, если вы гоняетесь за тестами:

  • Новые динамические выделения памяти для объектов {}или массивов [](например, для splitсоздаваемого) будут стоить очень дорого.
  • RegExp поиск более сложен и поэтому медленнее, чем поиск строк.
  • Если у вас уже есть массив, деструктурирование массивов происходит примерно так же быстро, как и их явная индексация, и выглядит потрясающе.

Удаление за пределы первой инстанции

Вот решение, которое будет нарезано вплоть до n-го экземпляра. Это не так быстро, но, по мнению ОП, gobble(element, '_', 1)все еще> в 2 раза быстрее, чем решение RegExpили, splitи может сделать больше:

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

С приведенным выше определением, gobble('path/to/file.txt', '/')даст имя файла и gobble('prefix_category_item', '_', 1)удалит префикс, как первое решение в этом ответе.


  1. Тесты проводились в Chrome 70.0.3538.110 на macOSX 10.14.
Хаим Лейб Хальберт
источник
Да ладно ... Сейчас 2019 год. Есть ли на самом деле люди, которые до сих пор используют микробенчмаркинг?
Виктор Шредер,
Я согласен. Хотя микробенчмаркинг немного интересен, вы должны полагаться на компилятор или транслятор для оптимизации. Кто знает. Мб кто-то читает это, строит компилятор или использует ejs / embedded и не может использовать регулярные выражения. Тем не менее, это выглядит лучше для моего конкретного случая, чем регулярное выражение. (Я бы убрал «самое быстрое решение»)
TamusJRoyce
1

Решение Марка Ф. является замечательным, но оно не поддерживается старыми браузерами. Решение Kennebec великолепно и поддерживается старыми браузерами, но не поддерживает регулярные выражения.

Итак, если вы ищете решение, которое разбивает вашу строку только один раз, которое поддерживается старыми браузерами и поддерживает регулярные выражения, вот мое решение:

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

pmrotule
источник
1

Для такого новичка, как я, который не привык к регулярным выражениям, это обходное решение сработало:

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

Метод slice () извлекает часть строки и возвращает новую строку, а метод indexOf () возвращает позицию первого найденного вхождения указанного значения в строке.

MZulkarnain Jaranee
источник
Это не обходной путь, а правильный способ сделать это;)
Виктор Шредер,
1

Используйте строковый replace()метод с регулярным выражением :

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

Это регулярное выражение соответствует 0 или более символам перед первым _и _самим собой. Затем совпадение заменяется пустой строкой.

Джеймс Т
источник
document.body.innerHTMLЧасть здесь совершенно бесполезно.
Виктор Шредер
@ VictorSchröder, как вы ожидаете увидеть вывод фрагмента без document.body.innerHTML?
Джеймс Т
2
document.bodyзависит от наличия DOM и не будет работать в чистой среде JavaScript. console.logдля этого достаточно или просто оставьте результат в переменной для проверки.
Виктор Шредер
@ VictorSchröder Не думаю, что это вызвало бы много путаницы, но я все же отредактировал.
Джеймс Т
0

Это работало для меня на Chrome + FF:

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

Если вам также нужен ключ, попробуйте это:

"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)
oriadam
источник
0

Вот один RegExp, который делает трюк.

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

Сначала это заставляет матч начинаться с начала с '^'. Затем он соответствует любому количеству символов, которые не являются «_», другими словами, все символы перед первым «_».

'?' означает, что минимальное количество символов, которые соответствуют целому шаблону, совпадает с '. *?' потому что за ним следует '_', который затем включается в совпадение в качестве последнего символа.

Поэтому этот метод split () использует такую ​​подходящую часть, как «сплиттер», и удаляет ее из результатов. Таким образом, он удаляет все, вплоть до первого «_», и дает вам остальное как 2-й элемент результата. Первый элемент "" представляет часть перед соответствующей частью. Это "", потому что матч начинается с начала.

Есть и другие RegExps, которые работают так же, как /_(.*)/, данные Чанду в предыдущем ответе.

Преимущество /^.*?_/ заключается в том, что вы можете понять, что он делает, не зная особой роли групп захвата, выполняемых с replace ().

Panu Logic
источник