Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   как выделять последовательности искомых подстрок? (https://javascript.ru/forum/events/79074-kak-vydelyat-posledovatelnosti-iskomykh-podstrok.html)

Adrikks 13.12.2019 17:21

как выделять последовательности искомых подстрок?
 
Подскажите, как выделять последовательности искомых подстрок?
Исходная строка: <br><input type="text" id="text" placeholder="Введите текст..."><br/><br/>
Искомые совпадения: <br><input type="text" name="search" id="search" placeholder="Введите текст...">
<p id="result"></p>

search.oninput = function() {
   //подскажите, как это можно сделать
   }

рони 13.12.2019 18:27

поиск последовательности слов
 
Adrikks,
при условии отсутствия служебных символов, иначе нужно добавить экранирование.
<!DOCTYPE html>
<html>
<head>
  <title>Untitled</title>
  <meta charset="utf-8">
  <style type="text/css">
    label{
        display: flex;
        flex-direction: column;
    }
    #result b{
        color: #FF0000;
    }

  </style>
</head>
<body>
<label>Исходная строка: <input type="text" id="text" placeholder="Введите текст..." value="ЗАКИРОВ АМИРЗЯН ГАЗИЗЬЯНОВИЧ"></label>
<label>Искомые совпадения: <input type="text" name="search" id="search" placeholder="Введите текст..." value="з а г"></label>
<p id="result"></p>
<script>
search.addEventListener('input', _=>{
    let txt = text.value, patern = search.value.trim();
    if(txt && patern) {
    let ar = patern.split(/\s+/);
    let reg = ar.map(_=> `(^|\\s+)(${_})(\\S*)`);
    patern = new RegExp(reg.join(''), 'i');
    txt = txt.replace(patern, a => ar.reduce((a,_, i) => {
    patern = new RegExp(reg[i], 'i');
    return a.replace(patern, '$1<b>$2</b>$3')
    }, a)
    )
    };
    result.innerHTML = txt;
})
//для теста
let event = new Event("input");
    search.dispatchEvent(event);

</script>

</body>
</html>

Adrikks 13.12.2019 18:46

рони,
а как совместить, чтобы не только с начала строки искались последовательности, а допустим в исходной строке будет "спасибо" и при искомой "си" выделило бы их в результирующей строчке?

рони 13.12.2019 19:01

Цитата:

Сообщение от Adrikks
с начала строки искались последовательности

ищется любая последовательность начала слов в строке, а не только сначала строки.
Цитата:

Сообщение от Adrikks
а как совместить,

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

примеры что на входе, что на выходе.

Adrikks 13.12.2019 19:06

рони,
Понимаю. Попробую уточнить.

рони 13.12.2019 19:14

Adrikks,
пишите текстом, а не картинкой. :)

рони 13.12.2019 19:16

Adrikks,
<!DOCTYPE html>
<html>
<head>
  <title>Untitled</title>
  <meta charset="utf-8">
  <style type="text/css">
    label{
        display: flex;
        flex-direction: column;
    }
    #result b{
        color: #FF0000;
    }

  </style>
</head>
<body>
<label>Исходная строка: <input type="text" id="text" placeholder="Введите текст..." value="спасибо ЗАКИРОВ АМИРЗЯН ГАЗИЗЬЯНОВИЧ главный врач главный врач"></label>
<label>Искомые совпадения: <input type="text" name="search" id="search" placeholder="Введите текст..." value="си з мир г"></label>
<p id="result"></p>
<script>
search.addEventListener('input', _=>{
    let txt = text.value, patern = search.value.trim();
    if(txt && patern) {
    let ar = patern.split(/\s+/);
    let reg = ar.map(_=> `(^\\S*|\\s+\\S*)(${_})(\\S*)`);
    patern = new RegExp(reg.join(''), 'ig');
    txt = txt.replace(patern, a => ar.reduce((a,_, i) => {
    patern = new RegExp(reg[i], 'i');
    return a.replace(patern, '$1<b>$2</b>$3')
    }, a)
    )
    };
    result.innerHTML = txt;
})
let event = new Event("input");
    search.dispatchEvent(event);

</script>

</body>
</html>

Adrikks 13.12.2019 19:17

рони,
Вот не знаю как правильно объяснить, как именно должно работать)
Поэтому показываю на примере картинки, каким должен быть результат)

рони 13.12.2019 19:19

Adrikks,
пример выше, смотрите.

Adrikks 13.12.2019 19:21

рони,
Да, думаю это именно то, что мне нужно было)
Спасибо за ваше время :)

Malleys 13.12.2019 22:01

рони, если поискать «с», то находит вторую «с» в слове спасибо. Также всё ломается на специальных символах и в случае, если ввести HTML-код...

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

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<style>
		label {
			display: flex;
			flex-direction: column;
		}
		#result mark {
			color: #FF0000;
		}
	</style>
</head>
<body>
	<label>
		Исходная строка:
		<input id="text" placeholder="Введите текст..." value="спасибо ЗАКИРОВ АМИРЗЯН ГАЗИЗЬЯНОВИЧ главный врач главный врач">
	</label>
	<label>
		Искомые совпадения:
		<input id="search" placeholder="Введите текст..." value="си з мир г">
	</label>
	<p id="result"></p>
	<script>
RegExp.escape = s => s.replace(/[[\\^$.|?*+()]/gim, "\\$&");
function mark(string) {
	const node = document.createElement("mark");
	node.textContent = string;
	return node;
}
search.addEventListener("input", () => {
	const value = text.value;
	const parts = search.value.trim().split(/\s+/);
	const pattern = new RegExp(parts.map(RegExp.escape).join("|"), "gi");
	let match, index = 0;

	result.textContent = "";

	if(parts[0].length > 0) while(match = pattern.exec(value))
		result.append(
			value.slice(index, index = match.index),
			mark(value.slice(index, index = pattern.lastIndex))
		);

	result.append(value.slice(index));
});
search.dispatchEvent(new Event("input"));
</script>
</body>
</html>

рони 13.12.2019 22:27

Malleys,
не вижу преимуществ, не понимаю логики выделения(на мой взгляд в вашем варианте выделяется больше чем нужно), экранизацию предлагал выше, если нужна будет необходимость.
и спасибо за превосходный код.
<input id="search" placeholder="Введите текст..." value="си з мир г">
<p id="result">спа<mark>си</mark>бо <mark>З</mark>АКИРОВ А<mark>МИР</mark><mark>З</mark>ЯН <mark>Г</mark>А<mark>З</mark>И<mark>З</mark>ЬЯНОВИЧ <mark>г</mark>лавный врач <mark>г</mark>лавный врач</p></body>

<p id="result">спа<mark>си</mark>бо <mark>З</mark>АКИРОВ А<mark>МИР</mark>ЗЯН <mark>Г</mark>АЗИЗЬЯНОВИЧ главный врач главный врач</p></body>

си з мир г
4 слова последовательно содержащие, 1 слово си, 2 слово з и т.д. если будут ещё 4 слова с таким содержанием в этой же строке, выделить снова так же
Цитата:

Сообщение от Malleys
вставлять текст на страницу именно как текст!

это не понимаю.

Malleys 13.12.2019 22:34

Цитата:

Сообщение от рони
4 слова последовательно содержащие, 1 слово си, 2 слово з и т.д. если будут ещё 4 слова с таким содержанием в этой же строке, выделить снова так же

Да, вы можете использовать рег. выр. из своего примера.

Я показал, как экранировать сп. символы, и как вставить текст на страницу!

Цитата:

Сообщение от рони
это не понимаю.

Ну если вставлять текст на страницу через innerHTML, то текст, который содержит разметку, применяется на странице... Я думаю, что если исходный текст содержал разметку, то вы хотите и в результате увидеть именно исходный текст (но с нужным выделением), а не применённую разметку.

рони 13.12.2019 22:53

Цитата:

Сообщение от Malleys
Ну если вставлять текст на страницу через innerHTML, то текст, который содержит разметку, применяется на странице... Я думаю, что если исходный текст содержал разметку, то вы хотите и в результате увидеть именно исходный текст (но с нужным выделением), а не применённую разметку.

инпут не может содержать разметку, или я чего не знаю.
но как расширение

перечитал ещё раз, не знаю о чём вы пишите.

рони 13.12.2019 23:21

ещё вариант поиска, при условии пробел - слово - пробел ...
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style>
        label {
            display: flex;
            flex-direction: column;
        }
        #result mark {
            color: #FF0000;
        }
    </style>
</head>
<body>
    <label>
        Исходная строка:
        <input id="text" placeholder="Введите текст..." value="спасибо ЗАКИРОВ АМИРЗЯН ГАЗИЗЬЯНОВИЧ главный врач главный врач">
    </label>
    <label>
        Искомые совпадения:
        <input id="search" placeholder="Введите текст..." value="гл в">
    </label>
    <p id="result"></p>
    <script>
RegExp.escape = s => s.replace(/[[\\^$.|?*+()]/gim, "\\$&");

search.addEventListener("input", () => {
    const value = text.value.trim().split(/\s+/);
    const parts = search.value.trim().split(/\s+/).map(s => s.toLowerCase());
    const length = parts.length;
    for (let i = 0; i <= value.length - length; i++) {
        let found = value.slice(i, i + length).every((txt, k) => txt.toLowerCase().includes(parts[k]));
        if(found) {
            parts.forEach((pattern, k) => value[k + i] = value[k + i].replace(new RegExp(RegExp.escape(pattern), "i"), "<mark>$&</mark>"));
            i += length - 1;
        }
    }

    result.innerHTML = value.join(" ");

});
search.dispatchEvent(new Event("input"));
</script>
</body>
</html>

Malleys 13.12.2019 23:47

Цитата:

Сообщение от рони
перечитал ещё раз, не знаю о чём вы пишите.

Судя по последнему примеру, вы действительно не видите возможности XSS и др. проблем.

Например, возьмите в качестве исходного текста такой —
Код:

ГАЗИЗЬЯНОВИЧ рулит — главный врач Метки: <врач>, <the-best>
или...
Код:

А вы знали, что элемент <mark> используется для подсветки найденного текста?
или...
Код:

<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:white;">Dicks, Pussies and Assholes</div>

рони 14.12.2019 00:03

Malleys,
ок подумаю.

рони 14.12.2019 00:27

Malleys,
может есть иной способ, пока так
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style>
        label {
            display: flex;
            flex-direction: column;
        }
        #result mark {
            color: #FF0000;
        }
    </style>
</head>
<body>
    <label>
        Исходная строка:
        <input id="text" placeholder="Введите текст..." value="А вы знали, что элемент <mark> используется для подсветки найденного текста?">
    </label>
    <label>
        Искомые совпадения:
        <input id="search" placeholder="Введите текст..." value="п нн кс">
    </label>
    <p id="result"></p>
    <script>
RegExp.escape = s => s.replace(/[[\\^$.|?*+()]/gim, "\\$&");
function mark(string) {
    const node = document.createElement("mark");
    node.textContent = string;
    return node;
}
search.addEventListener("input", () => {
    const value = text.value.trim().split(/\s+/);
    const parts = search.value.trim().split(/\s+/).map(s => s.toLowerCase());
    const length = parts.length;
    result.textContent = "";
    for (let i = 0; i < value.length; i++) {
        let found = value.slice(i, i + length).every((txt, k) => txt.toLowerCase().includes(parts[k]));
        if(found) {
            parts.forEach((pattern, k) => value[k + i].replace(new RegExp("(\\S*?)(" +RegExp.escape(pattern) +")(\\S*)", "i"), (a,b,c,d) => result.append(
            b,
            mark(c),
            d,
            " "
        )));
            i += length - 1;
        }
        else result.append(value[i], " ")
    }


});
search.dispatchEvent(new Event("input"));
</script>
</body>
</html>

Цитата:

Сообщение от Malleys
Да, вы можете использовать рег. выр. из своего примера.

не знаю как

Adrikks 15.12.2019 18:03

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

в этой ситуации должны выделиться и ГЛавный сан ВРач

рони 15.12.2019 18:23

Adrikks,
а нельзя без картинок?
скопируйте строки и выделите нужные совпадения
исходная строка
главный врач 12345 главный врач
искомые совпадения
гл вр
главный врач 12345 главный врач
:-?

Adrikks 15.12.2019 18:28

рони,
ахах, хорошо, не буду больше присылать картинки, раз Вам это не нравится)
Думал так понятнее для отображения ожидаемого результата)
Пример:
исходная строка
главный врач 12345 главный сан врач
искомые совпадения
гл вр
главный врач 12345 главный сан врач
Вот пример с картинки) Такое не сработает на данный момент если добавить слово между "главный" и "врач"

рони 15.12.2019 18:44

Adrikks,
картинка это замечательно пусть будет, но
для проверки необходим текст.

Adrikks 15.12.2019 19:26

рони,
Понял)
В общем суть в том, что должны выделяться последовательности искомых подстрок вне зависимости от кол-ва символов между ними
Был бы очень благодарен, если бы подсказали как это можно сделать

рони 15.12.2019 19:44

Adrikks,
нужен алгоритм. :) пока не знаю короткой дороги.

рони 15.12.2019 22:09

поиск совпадений в последовательности слов
 
Adrikks,

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style>
        label {
            display: flex;
            flex-direction: column;
        }
        #result mark {
            color: #FF0000;
        }
    </style>
</head>
<body>
    <label>
        Исходная строка:
        <input id="text" placeholder="Введите текст..." value="главный врач 12345 главный сан врач">
    </label>
    <label>
        Искомые совпадения:
        <input id="search" placeholder="Введите текст..." value="гл вр">
    </label>
    <p id="result"></p>
    <script>
RegExp.escape = s => s.replace(/[[\\^$.|?*+()]/gim, "\\$&");
function mark(string) {
    const node = document.createElement("mark");
    node.textContent = string;
    return node;
}
search.addEventListener("input", () => {
    const value = text.value.match(/\s+|\S+/g);
    let parts = search.value.trim();
    const length = parts.length;
    if(!value || !length) {result.textContent = text.value; return};
    parts = parts.split(/\s+/).map(pattern => new RegExp("(\\S*?)(" +RegExp.escape(pattern) +")(\\S*)", "i"));
    result.textContent = "";
    let temp = [];
    for (let i = 0; i < value.length; ) {
        let k = i, arrFindIndex = [];
        let found = parts.every(reg => {
        let index = value.slice(k).findIndex(el => reg.test(el));
        if(index === -1 ) return false;
        k += index;
        arrFindIndex.push({k,reg});
        k++;
        return true
        });
        if(found) {i = k; temp.push(...arrFindIndex)}
        else i++;
    }
    value.forEach((el, i) => {
    const find = temp.find(({k}) => k == i);
    if(find){
        const {reg} = find;
        [_, ...el]= el.match(reg);
        el[1] = mark(el[1])
        result.append(
          ...el
        )
    }
    else result.append(el)
    })

});
search.dispatchEvent(new Event("input"));
</script>
</body>
</html>

Adrikks 16.12.2019 08:32

рони,
последовательность из двух символов находится везде, а из одного допустим, только один раз
Пример как должно:
Исходный текст:
АЛИЕВА БАЙНАТ АБДУРАХМАНОВНА
Искомые совпадения:
а б
Результат:
АЛИЕВА БАЙНАТ АБДУРАХМАНОВНА

рони 16.12.2019 08:42

Цитата:

Сообщение от Adrikks
последовательность из двух символов находится везде, а из одного допустим, только один раз

я пас. вы третий раз изменили условия.

Adrikks 16.12.2019 11:38

рони,
Я понимаю) Я не знаю как вам объяснить всё это в одном алгоритме
По этому и кидал картинку

рони 16.12.2019 15:45

Adrikks,
Цитата:

Сообщение от Adrikks
Исходный текст:
АЛИЕВА БАЙНАТ АБДУРАХМАНОВНА
Искомые совпадения:
а б
Результат:
АЛИЕВА БАЙНАТ АБДУРАХМАНОВНА

результат вашего выделения неверный!!!
должно быть так
<p id="result"><mark>А</mark>ЛИЕВА <mark>Б</mark><mark>А</mark>ЙНАТ А<mark>Б</mark>ДУРАХМАНОВНА</p>

Adrikks 16.12.2019 16:12

рони,
Я вам показал, как по заданному мне заданию)
Ну хотя бы с вариантом представленным у вас, помогите пожалуйста)
Больше не побеспокою Вас

рони 16.12.2019 16:15

Adrikks,
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style>
        label {
            display: flex;
            flex-direction: column;
        }
        #result mark {
            color: #FF0000;
        }
    </style>
</head>
<body>
    <label>
        Исходная строка:
        <input id="text" placeholder="Введите текст..." value="АЛИЕВА БАЙНАТ АБДУРАХМАНОВНА">
    </label>
    <label>
        Искомые совпадения:
        <input id="search" placeholder="Введите текст..." value="а б">
    </label>
    <p id="result"></p>
    <script>
RegExp.escape = s => s.replace(/[[\\^$.|?*+()]/gim, "\\$&");
function mark(string) {
    const node = document.createElement("mark");
    node.textContent = string;
    return node;
}
search.addEventListener("input", () => {
    const value = text.value;
    let parts = search.value.trim();
    const length = parts.length;
    if(!value || !length) {result.textContent = text.value; return};
    parts = parts.split(/\s+/).map(pattern => new RegExp("(" +RegExp.escape(pattern) +")", "i"));
    result.textContent = "";
    let temp = [];
    for (let i = 0; i < value.length; ) {
        let k = i, arrFindIndex = [];
        let found = parts.every(reg => {
        reg.lastIndex = 0;
        let match = reg.exec(value.slice(k));
        if(match) {
        k += match.index
        arrFindIndex.push(k, k += match[0].length);
        return true
        }
        return false;
        });
        if(found) {i = k; temp.push(...arrFindIndex)}
        else i++;
    }
    let index = 0;
    for (let i = 0; i < temp.length; i += 2){
    result.append(
            value.slice(index, index = temp[i]),
            mark(value.slice(index, index = temp[i + 1]))
        );
    }
   result.append(value.slice(index));
});
search.dispatchEvent(new Event("input"));
</script>
</body>
</html>

Adrikks 16.12.2019 18:39

рони,
спасибо большое за помощь и за терпение.

JoenRules 17.12.2019 16:40

рони,
решаю подобную задачу. Можете объяснить, что вы делаете здесь?
RegExp.escape = s => s.replace(/[[\\^$.|?*+()]/gim, "\\$&");

и здесь
let found = parts.every(reg => {
        reg.lastIndex = 0;
        let match = reg.exec(value.slice(k));
        if(match) {
        k += match.index
        arrFindIndex.push(k, k += match[0].length);
        return true
        }
        return false;
        });
        if(found) {i = k; temp.push(...arrFindIndex)}
        else i++;
    }
    let index = 0;
    for (let i = 0; i < temp.length; i += 2){
    result.append(
            value.slice(index, index = temp[i]),
            mark(value.slice(index, index = temp[i + 1]))
        );
    }
   result.append(value.slice(index));

(комментарии возможно), если не трудно. Относительно недавно начала изучать JavaScript.

рони 17.12.2019 17:27

JoenRules,
RegExp.escape экранирование служебных символов https://learn.javascript.ru/regexp-escaping
. это любой символ в RegExp
\. это просто точка
если в строке поиска есть точка, нужно "уточнить" (экранировать) что это именно точка.
подробнее по ссылке.
let found = parts.every(reg => { // parts -> массив всех совпадений
       // every ->  каждое совпадение должно быть в проверяемой строке
        reg.lastIndex = 0; //искать с начала строки
        let match = reg.exec(value.slice(k));//проверка совпадения
        if(match) {//если совпадение есть
        k += match.index
        arrFindIndex.push(k, k += match[0].length); //запоминаем совпадение с такого символа по такой
        return true // продолжаем поиск
        }
        return false;// прекращаем поиск если хотябы одного совпадения нет
        });
        if(found) {i = k; temp.push(...arrFindIndex)} //если поиск был удачный сокращаем строку на длину совпадений
        // и сохраняем все сопадения
        else i++; //иначе сокращаем строку на один символ
        // продолжаем поиск по строке пока есть длина строки
    }
    let index = 0;//вывод результата с начала строки
    for (let i = 0; i < temp.length; i += 2){// цикл по участкам совпадения
    result.append(
            value.slice(index, index = temp[i]), // вывод строки до совпадения
            mark(value.slice(index, index = temp[i + 1]))// вывод элемента mark с текстом совпадения
        );
    }
   result.append(value.slice(index)); //вывод последнего участка строки


Часовой пояс GMT +3, время: 16:21.