Jak mogę przetwarzać każdą literę tekstu za pomocą Javascript?

361

Chciałbym ostrzec każdą literę ciągu, ale nie jestem pewien, jak to zrobić.

Więc jeśli mam:

var str = 'This is my string';

Chciałbym móc osobno alarmować T, h, i, s itp. To dopiero początek pomysłu, nad którym pracuję, ale muszę wiedzieć, jak przetwarzać każdą literę osobno.

Chcę użyć jQuery i pomyślałem, że może potrzebuję użyć funkcji podziału po przetestowaniu długości łańcucha.

Pomysły?

Nic Hubbard
źródło
3
Może tego szukałeś: od ES6 jest for(const c of str) { ... }. Więcej na ten temat poniżej w dość szczegółowej, ale niewystarczająco pozytywnej odpowiedzi. PS: Link @ ARJUN nie działa dla mnie.
Max

Odpowiedzi:

419

Jeśli kolejność alertów ma znaczenie, użyj tego:

for (var i = 0; i < str.length; i++) {
  alert(str.charAt(i));
}

Jeśli kolejność alertów nie ma znaczenia, użyj tego:

var i = str.length;
while (i--) {
  alert(str.charAt(i));
}

Eli Gray
źródło
2
używanie []znaku w celu uzyskania znaku w określonej pozycji nie jest obsługiwane w IE <9
vsync
13
zgodnie z drugą odpowiedzią, możesz użyć str.charAt (i) zamiast []. aby dowiedzieć się więcej o tym, dlaczego warto używać charAt vs [], zobacz string.charAt (x) lub string [x]
Julian Soro
12
Trudno mi uwierzyć, że jakikolwiek współczesny kompilator JS ponownie obliczy długość, jeśli łańcuch nie zostanie zmodyfikowany w pętli. W każdym innym języku chętnie sprawdzę długość w klauzuli testowej pętli for, zakładając, że kompilator wie najlepiej i odpowiednio go zoptymalizuje.
Echelon
3
@Dagmar: Javascript nie używa UTF-8, używa UTF-16 (lub UCS-2, w zależności od przeglądarki). Każdy pojedynczy znak może być reprezentowany jako UTF-8 lub UTF-16, ale nie ma tego problemu. Jedyne, które mają problem, to te, które wymagają czterech bajtów w UTF-16 zamiast dwóch bajtów. 💩 jest znakiem wymagającym czterech bajtów w UTF-16. Kluczowymi terminami, na które można znaleźć więcej informacji, są „płaszczyzna astralna”, „non-BMP” i „para zastępcza”.
hippietrail
1
@Dagmar: Java i Javascript mają wspólne UTF-16 (wcześniej UCS-). Trzecią główną platformą, która z niego korzysta, jest system Windows. Protokoły Unix, MacOS i internetowe używają UTF-8. charAtpozostało z UCS-2 dni, kiedy nie było żadnych par zastępczych i aby rozwiązać problem, dodano nową funkcję codepointAtdo JavaScript, która poprawnie obsługuje nasz przyjazny stos kupek. Wierzę, że Java też to ma.
hippietrail
240

Prawdopodobnie jest to więcej niż rozwiązane. Chcę tylko pomóc w innym prostym rozwiązaniu:

var text = 'uololooo';

// With ES6
[...text].forEach(c => console.log(c))

// With the `of` operator
for (const c of text) {
    console.log(c)
}

// With ES5
for (var x = 0, c=''; c = text.charAt(x); x++) { 
    console.log(c); 
}

// ES5 without the for loop:
text.split('').forEach(function(c) {
    console.log(c);
});
Pan Goferito
źródło
4
ostatnim przykładem może być po prostu[...text].forEach(console.log)
Govind Rai
10
Nie, nie może. forEach()przekazuje indeks i tablicę jako drugi i trzeci argument. Wolałbym tego nie rejestrować ...
Pan Goferito
1
Zauważ, że zarówno operator rozkładania (pierwszy przykład), jak i połączenie dzielone (ostatni przykład) utworzą nową tablicę. Zwykle nie stanowi to problemu, ale może być kosztowne w przypadku dużych łańcuchów lub częstych zastosowań.
Randolpho,
Cofor (let c of [...text]) { console.log(c) }
Flimm
Dzięki temu tworzysz nową tablicę z łańcucha. Nie widzę korzyści. let c of textjuż wykonuje swoją pracę.
Pan Goferito,
73

Jedno możliwe rozwiązanie w czystym javascript:

for (var x = 0; x < str.length; x++)
{
    var c = str.charAt(x);
    alert(c);
}
miku
źródło
Prawdopodobnie byłoby lepiej z var x = 0 i var c = str.charAt (x).
Bogaty
2
Ponadto str.length powinien być przechowywany w zmiennej, aby nie trzeba było do niego uzyskiwać dostępu.
Eli Gray,
8
@EliGrey Czy to naprawdę tak ważne, aby wstawiać długość w zmiennej? Czy masz testy porównawcze, kiedy byłoby to lepsze niż posiadanie mniejszej liczby wierszy kodu?
pm_labs
@paul_sns Co ciekawe, wydaje się, że istnieje niewielka różnica, przynajmniej w Edge (różnica 0,7 ms dla tablicy 10000 elementów): jsfiddle.net/carcigenicate/v8vvjoc1/1 . Prawdopodobnie nie jest to idealny test, ale opiera się on na średnio 10000 testach.
Carcigenicate
1
@paul_sns Co ciekawe, Chrome wykonał ten sam test przez około 2% czasu (~ 5 ms vs ~ 0,0997 ms), a obie wersje dały ten sam czas, więc wygląda na to, że Edge nie jest zoptymalizowany.
Carcigenicate
69

Jak przetwarzać każdą literę tekstu (z testami porównawczymi)

https://jsperf.com/str-for-in-of-foreach-map-2

dla

Klasyczny i zdecydowanie najbardziej wydajny . Powinieneś iść z tym, jeśli planujesz używać go w algorytmie krytycznym pod względem wydajności lub że wymaga on maksymalnej kompatybilności z wersjami przeglądarki.

for (var i = 0; i < str.length; i++) {
  console.info(str[i]);
}

dla ... z

for ... of to nowy ES6 dla iteratora. Obsługiwane przez większość nowoczesnych przeglądarek. Jest to bardziej atrakcyjne wizualnie i mniej podatne na błędy w pisaniu. Jeśli wybierasz ten w aplikacji produkcyjnej, prawdopodobnie powinieneś użyć transpilatora takiego jak Babel .

let result = '';
for (let letter of str) {
  result += letter;
}

dla każdego

Podejście funkcjonalne . Zatwierdzony przez Airbnb . Największym minusem robienia tego w ten sposób jest to split(), że tworzy nową tablicę do przechowywania każdej pojedynczej litery łańcucha.

Dlaczego? To wymusza naszą niezmienną zasadę. Radzenie sobie z czystymi funkcjami zwracającymi wartości jest łatwiejsze do uzasadnienia niż skutki uboczne.

// ES6 version.
let result = '';
str.split('').forEach(letter => {
  result += letter;
});

lub

var result = '';
str.split('').forEach(function(letter) {
  result += letter;
});

Poniżej znajdują się te, których nie lubię.

dla w

W przeciwieństwie do ... z, zamiast litery dostajesz indeks listów. Działa dość źle.

var result = '';
for (var letterIndex in str) {
  result += str[letterIndex];
}

mapa

Podejście funkcyjne, co jest dobre. Jednak mapa nie jest do tego przeznaczona. Należy go użyć, gdy trzeba zmienić wartości wewnątrz tablicy, co nie ma miejsca.

// ES6 version.
var result = '';
str.split('').map(letter => {
  result += letter;
});

lub

let result = '';
str.split('').map(function(letter) {
  result += letter;
});
zurfyx
źródło
1
Na mojej maszynie klasyczna forpętla była w rzeczywistości drugą najwolniejszą, podczas gdy for...ofbyła najszybsza (około trzy razy szybciej niż for).
John Montgomery
1
Gdzie jest punkt odniesienia? Jakie jest najszybsze rozwiązanie?
poitroae
1
@johnywhy To było dwa lata temu, a link jest martwy, więc nie jestem pewien, jak oczekujesz ode mnie obrony wyniku, który wtedy uzyskałem. Utworzenie nowego testu porównawczego zgadza się jednak z wnioskiem zurfyx, przy czym forpętla jest nieco szybsza.
John Montgomery
1
@JohnMontgomery Nie oczekuję, że zrobisz coś. Uwaga dla przyszłych czytelników, że twoje wyniki są inne niż odpowiedź. Osobiście chciałbym wiedzieć, które wyniki odnoszą się do przeglądarek dzisiaj 2020, choć rok 2018 nie był tak dawno temu. Który link jest martwy?
John, dlaczego
1
@ johnywhy Link na górze wszystkich aktualnych testów zwraca mi 404.
John Montgomery
42

Większość, jeśli nie wszystkie, odpowiedzi tutaj są błędne, ponieważ ulegną one zepsuciu, gdy w ciągu znaków znajdzie się znak spoza Unicode BMP (Basic Multilingual Plane) . Oznacza to, że wszystkie emoji zostaną zepsute .

JavaScript używa UTF- 16 Unicode dla wszystkich ciągów. W UTF-16 postacie poza BMP składają się z dwóch części, zwanych „ parą zastępczą ”, a większość odpowiedzi tutaj przetworzy każdą część takich par indywidualnie, a nie jako pojedynczy znak.

Jednym ze sposobów we współczesnym JavaScript od co najmniej 2016 roku jest użycie nowego iteratora ciągów . Oto przykład (prawie) prosto z MDN:

var string = 'A\uD835\uDC68B\uD835\uDC69C\uD835\uDC6A';

for (var v of string) {
  alert(v);
}
// "A"
// "\uD835\uDC68"
// "B"
// "\uD835\uDC69"
// "C"
// "\uD835\uDC6A"

hippietrail
źródło
4
Aby zapoznać się z nowoczesnym rozwiązaniem dzielenia łańcucha na znaki, biorąc pod uwagę pary zastępcze, zobacz: stackoverflow.com/a/42596897/527702
hippietrail
20

Możesz tego spróbować

var arrValues = 'This is my string'.split('');
// Loop over each value in the array.
$.each(arrValues, function (intIndex, objValue) {
    alert(objValue);
})
Adriaan Stander
źródło
11
Nadal jest opcją, ale nie wydajną. Nie umieszczaj jQuery wszędzie.
cagatay
10

Jeszcze jedno rozwiązanie ...

var strg= 'This is my string';
for(indx in strg){
  alert(strg[indx]);
}
Pamsix
źródło
3
Jeśli chcesz tylko znaku, a nie indeksu, szybciej byłoby użyć for..ofpętlifor (let ch of t) { alert(ch) }
Shaheen Ghiassy
10

Kiedy muszę napisać krótki kod lub linijkę, używam tego „hacka”:

'Hello World'.replace(/./g, function (char) {
    alert(char);
    return char; // this is optional 
});

To nie liczy nowych linii, więc może to być dobra lub zła rzecz. Jeśli które należą do nowej linii, należy wymienić: /./z /[\S\s]/. Drugi wkładki można zobaczyć prawdopodobnie używać .split()który ma wiele problemów

Downgoat
źródło
najlepsza odpowiedź. Bierze pod uwagę problemy z Unicode, a także może być używany z funkcjonalnymi konstrukcjami z .map () itp.
rofrol
Jedyne, co mi się nie podoba w tym, to to, że chcę uzyskać dostęp do dodatkowych parametrów przekazanych do funkcji forEachwywołania w porównaniu do wysłanych parametrówreplace . Jeśli wiem, że ASCIIing, myślę, że wciąż mam kilka przypadków użycia split. Świetna odpowiedź!
ruffin
Ta odpowiedź ma tę
zaletę, że wybiera
1
Myślałem, że nie uwzględniłoby to problemów związanych z Unicode, gdyby nie miał uflagi wraz z gflagą? OK właśnie przetestowałem i miałem rację.
hippietrail
9

Nowy JS pozwala na:

const str = 'This is my string';
Array.from(str).forEach(alert);
papajson
źródło
8

Lepiej jest użyć instrukcji for ..., jeśli ciąg znaków zawiera znaki Unicode, ze względu na inny rozmiar bajtu.

for(var c of "tree 木") { console.log(c); }
//"𝐀A".length === 3
Martin Wantke
źródło
7

krótka odpowiedź: Array.from(string) da ci to, czego prawdopodobnie chcesz, a następnie będziesz mógł iterować na nim lub cokolwiek innego, ponieważ jest to tylko tablica.

ok, spróbujmy z tym ciągiem: abc|⚫️\n⚪️|👨‍👩‍👧‍👧 .

punkty kodowe to:

97
98
99
124
9899, 65039
10
9898, 65039
124
128104, 8205, 128105, 8205, 128103, 8205, 128103

więc niektóre znaki mają jeden punkt kodowy (bajt), a niektóre mają dwa lub więcej, a nowy wiersz został dodany do dodatkowych testów.

więc po przetestowaniu są dwa sposoby:

  • byte per byte (codepoint per codepoint)
  • grupy znaków (ale nie emoji całej rodziny)

string = "abc|⚫️\n⚪️|👨‍👩‍👧‍👧"

console.log({ 'string': string }) // abc|⚫️\n⚪️|👨‍👩‍👧‍👧
console.log({ 'string.length': string.length }) // 21

for (let i = 0; i < string.length; i += 1) {
  console.log({ 'string[i]': string[i] }) // byte per byte
  console.log({ 'string.charAt(i)': string.charAt(i) }) // byte per byte
}

for (let char of string) {
  console.log({ 'for char of string': char }) // character groups
}

for (let char in string) {
  console.log({ 'for char in string': char }) // index of byte per byte
}

string.replace(/./g, (char) => {
  console.log({ 'string.replace(/./g, ...)': char }) // byte per byte
});

string.replace(/[\S\s]/g, (char) => {
  console.log({ 'string.replace(/[\S\s]/g, ...)': char }) // byte per byte
});

[...string].forEach((char) => {
  console.log({ "[...string].forEach": char }) // character groups
})

string.split('').forEach((char) => {
  console.log({ "string.split('').forEach": char }) // byte per byte
})

Array.from(string).forEach((char) => {
  console.log({ "Array.from(string).forEach": char }) // character groups
})

Array.prototype.map.call(string, (char) => {
  console.log({ "Array.prototype.map.call(string, ...)": char }) // byte per byte
})

var regexp = /(?:[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g

string.replace(regexp, (char) => {
  console.log({ 'str.replace(regexp, ...)': char }) // character groups
});

localhostdotdev
źródło
7

Możesz teraz iterować poszczególne punkty kodu Unicode zawarte w ciągu znaków, używając String.prototype[@@iterator], który zwraca wartość dobrze znanego typu Symbol Symbol.iterator- domyślny iterator dla obiektów podobnych do tablicy ( Stringw tym przypadku).

Przykładowy kod:

const str = 'The quick red 🦊 jumped over the lazy 🐶! 太棒了!';

let iterator = str[Symbol.iterator]();
let theChar = iterator.next();

while(!theChar.done) {
  console.log(theChar.value);
  theChar = iterator.next();
}

// logs every unicode character as expected into the console.

Działa to ze znakami Unicode, takimi jak emoji lub znaki inne niż rzymskie, które wyzwalałyby starsze konstrukcje.

Odniesienie: MDN Link do String.prototype @@ iterator .

Aditya MP
źródło
2
Zauważ, że możesz to zrobić w krótszy sposób, również z for ... ofpętlą na łańcuchu - to znaczy cukier składniowy umożliwiający dostęp do iteratora.
Aditya, poseł
6

Teraz można używać w kluczowych.

    var s = 'Alien';
    for (var c in s) alert(s[c]);

mih0vil
źródło
Używanie w to zła praktyka i okropne, gdy nie są
filtrowane
4
@Downgoat dlaczego? Co w tym złego? Chodzi mi o to, że jestem w sytuacji, w której wiem, że mój silnik Javascript obsługuje słowo „in”, a mój kod nie znajdzie się w innym silniku… dlaczego tego nie użyć?
TKoL
@TKoL Zobacz to .
Alan
@Alan injest uzasadnioną częścią języka. Używaj rzeczy odpowiednio. Twój artykuł ostrzega, że ininterpretuje klawisze alfa tak samo jak klawisze numeryczne. Więc? Może tego właśnie chcesz. Można również powiedzieć, że inne metody niepoprawnie ignorują klawisze alfa. Imo ofma prawidłowe zachowanie. W tablicach JS elementy bez kluczy alfa nadal mają klucze: numeryczne. W mojej konsoli JS „poprawnie” traktuje klawisz alfa tak samo jak klawisze numeryczne:>const arr = ['a', 'b'] >arr.test = 'hello' >arr 0: "a" 1: "b" test: "hello" length: 2
John, dlaczego
5

Możesz uzyskać tablicę takich postaci

var test = "test string",
    characters = test.split('');

a następnie zapętlić za pomocą zwykłego Javascript, w przeciwnym razie możesz iterować znaki ciągu za pomocą jQuery przez

var test = "test string";

$(test.split('')).each(function (index,character) {
    alert(character);
});
Bogaty
źródło
5

możesz przekonwertować ten ciąg znaków na tablicę znaków przy użyciu split(), a następnie iterować go.

const str = "javascript";
const strArray = str.split('');

strArray.map(s => console.log(s));

Muhammed Moussa
źródło
najwyraźniej zawodzi to w przypadku znaków Unicode i symboli graficznych.
John, dlaczego
4

Jeśli chcesz wykonać transformację tekstu na poziomie znaków i odzyskać przekształcony tekst na końcu, możesz zrobić coś takiego:

var value = "alma";
var new_value = value.split("").map(function(x) { return x+"E" }).join("")

Więc kroki:

  • Podziel ciąg na tablicę (listę) znaków
  • Mapuj każdą postać za pomocą funktora
  • Połącz wynikową tablicę znaków w wynikowy ciąg
Vajk Hermecz
źródło
0

W dzisiejszym JavaScript możesz

Array.prototype.map.call('This is my string', (c) => c+c)

Oczywiście c + c reprezentuje cokolwiek chcesz zrobić z c.

To zwraca

["TT", "hh", "ii", "ss", " ", "ii", "ss", " ", "mm", "yy", " ", "ss", "tt", "rr", "ii", "nn", "gg"]

Pum Walters
źródło
Możliwe:[...'This is my string'].map((c)=>c+c)
Alan
0

Powinno to działać w starszych przeglądarkach i ze znakami UTF-16, takimi jak 💩.

To powinno być najbardziej kompatybilne rozwiązanie. Jest jednak mniej wydajna niż forpętla.

Wygenerowałem wyrażenie regularne za pomocą regexpu

var str = 'My String 💩 ';
var regEx = /(?:[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g


str.replace(regEx, function (char) {
    console.log(char)
});

Mam nadzieję że to pomoże!

Ben Gubler
źródło
Co rozumiesz przez „mniej perfomanta”? Myślę, że masz na myśli „wolniejszy”, ponieważ jest on bardziej zgodny z wymaganiami i działa dobrze.
hippietrail
-1

Możesz uzyskać dostęp do pojedynczych znaków za pomocą str.charAt(index)lub str[index]. Ale ten drugi sposób nie jest częścią ECMAScript, więc lepiej przejdź do poprzedniego.

Gumbo
źródło
Trzymałbym się od tego z daleka. Niestety nie działa to we wszystkich wersjach IE. Zaufaj mi. Nauczyłem się tego na własnej skórze.
Xavi
3
Jest częścią ECMAScript, ale tylko w nowo wydanej 5. edycji, a nie 3..
kangax,
-1

Jeśli chcesz animować każdą postać, może być konieczne zawinięcie jej w element span;

var $demoText = $("#demo-text");
$demoText.html( $demoText.html().replace(/./g, "<span>$&amp;</span>").replace(/\s/g, " "));

Myślę, że to najlepszy sposób, aby to zrobić, a następnie przetworzyć zakresy. (na przykład z TweenMax)

TweenMax.staggerFromTo ($ demoText.find („span”), 0,2, {autoAlpha: 0}, {autoAlpha: 1}, 0,1);

Chris Panayotoff
źródło
-1

Wypróbuj ten kod

    function myFunction() {
    var text =(document.getElementById("htext").value); 
    var meow = " <p> <,> </p>";
    var i;


    for (i = 0; i < 9000; i++) {

        text+=text[i] ;



    }

    document.getElementById("demo2").innerHTML = text;

}
</script>
<p>Enter your text: <input type="text" id="htext"/>

    <button onclick="myFunction();">click on me</button>
</p>
miauczeć
źródło