Тестируем юникод на ZennoPoster

  • Автор темы Автор темы mrarsen
  • Дата начала Дата начала

mrarsen

Client
Регистрация
30.12.2019
Сообщения
31
Реакции
6
Баллы
8
— Видишь суслика? — Нет. — И я не вижу. А он есть. (c) ДМБ

Введение​

Юникод (unicode) - это мощнейший технический стандарт определяющий представление, обработку, преобразование и визуальное отображение практически любой текстовой информации, призванный среди прочего обеспечить поддержку любого письменного языка когда-либо существовавшего в течение человеческой истории.
К настоящему моменту практически всё современное ПО каким-либо образом обрабатывающее текстовую информацию либо поддерживает юникод, либо внутренняя обработка текстовых данных в нём реализована на стандарте юникод. Одним из видов такого ПО являются браузеры, которые отображают конечным пользователям содержимое сайтов, работу с которыми мы с вами так любим автоматизировать. И в настоящее время абсолютное большинство сайтов работает на юникоде.
Сам стандарт среди прочего определяет огромную числовую кодовую таблицу для более чем миллиона символов. Однако совершенно неверно воспринимать юникод просто как огромную кодировочную таблицу. Юникод который по сути является множеством различных стандартов задаёт спецификации работы регулярных выражений, алгоритмы канонизации текстов, классы и свойства различных символов, изменение направления отображения потока текста и ещё много различных фич. Одной из интересных фич юникода являются так называемые неотображаемые символы. Присутствуя в юникод потоке они никак не отображаются визуально. Примерами таких символов являются пробел нулевой ширины, BOM, символы изменение направления отображения текста и прочие (в основном) технические символы. Будет ли определённый символ как-либо отображён или нет может зависеть от реализации в конкретном ПО. Думаю не надо объяснять уважаемым джентльменам, пользователям форума "сообщество профессионалов автоматизации" для чего такие символы нам могут быть полезны.
Однако чем больше что-то юзается тем менее это эффективно. Админы и программисты сайтов не дураки и уже давно сталкиваясь с подобными финтами закручивают гайки. Чем более "пабликовые" невидимые символы вы будете использовать тем будет меньше вероятность что это сработает. Чем больше невидимых символов вы сами сможете найти тем будет лучше.

Мой ZennoPoster шаблон практически полностью автоматизирует поиск невидимых в браузерах символов, оставляя минимум ручной работы.

Теория и базовые принципы работы​

Первый способ​

Давайте подумаем, как мы сможем технически проверить, отображается ли конкретный символ в html тексте или нет. Если в каком-то строчном html элементе (пусть это будет span) присутствует текст, то длина этого элемента будет зависеть от длины (именно от отображаемой длины) текста. Если в элементе будет присутствовать текст "abcdef", html код <span>abcdef</span>, то его длина будет больше чем у элемента с текстом "abc", html код <span>abc</span>. А вот если в элементе будет присутствовать текст "abc[НЕОТОБРАЖАЕМЫЙ ЮНИКОД СИМВОЛ]def", html код <span>abc[НЕОТОБРАЖАЕМЫЙ ЮНИКОД СИМВОЛ]def</span>, то его длина будет точно такой же как если бы в нём был текст "abcdef", html код <span>abcdef</span>. Это вся идея проверки. Тут есть подводные камни об этом чуть позже.

Более подробно​

Я не большой спец в html и javascript и моя реализация наверняка не самая лучшая. Я нашёл первый же работающий для меня способ и дальше юзал только его. Проверять мы будем не сам размер элемента с проверяемым текстом, а смещение элемента, идущего за ним от левого края родительского элемента и через это смещение косвенно определять искомый размер.

Пример html кода для наглядности:
HTML:
Развернуть Свернуть Копировать
<html>
 <body>
  <span>abcdef</span><span id=test1>//test1</span><Br>
  <span>abcXdef</span><span id=test2>//test2</span><Br>
  <span>abcXYdef</span><span id=test3>//test3</span><Br>
  <span>abcdef</span><span id=test4>//test4</span><Br>
  <script>
   alert('START');
   alert('test1: ' + document.getElementById('test1').offsetLeft);
   alert('test2: ' + document.getElementById('test2').offsetLeft);
   alert('test3: ' + document.getElementById('test3').offsetLeft);
   alert('test4: ' + document.getElementById('test4').offsetLeft);
  </script>
 </body>
</html>
span это строчный элемент и два подряд идущих span элемента будут размещаться на одной строке друг за другом. Соответственно чем больше будет длина отображаемого текста в первом span c текстом тем больше будет левое смещение следующего за ним span.
Если вы сохраните этот код в локальный файл, а затем откроете этот файл в браузере, то alert окна вам сообщат следующее: "test1: 51", "test2: 62", "test3: 74", "test4: 51" (конкретные числа у вас могут отличаться, но тут главное принцип).
Как вы можете видеть из кода alert окна сообщают левое смещение span - ов с id test1, test2, test3, test4 . Для span c id test1, перед которым стоит span c текстом "abcdef" смещение составляет 51. Для span c id test2 смещение уже больше: 62, ведь перед ним стоит span c более длинным отображаемым текстом "abcXdef". Для span с id test3 смещение станет ещё больше, ведь перед ним span с ещё более длинным отображаемым текстом "abcXYdef". Наконец для span c id test4 смещение будет точно таким же как для span с id test1 ведь перед ними обоими стоят span-ы с идентичными текстами, соответственно и с одинаковой отображаемой длиной.

Следующий пример html кода для наглядности:
HTML:
Развернуть Свернуть Копировать
<html>
 <head><meta charset="UTF-8"></head>
 <body>
  <span>abcdef</span><span id=test1>//test1</span><Br>
  <span>abcXdef</span><span id=test2>//test2</span><Br>
  <span>abc[НЕОТОБРАЖАЕМЫЙ ЮНИКОД СИМВОЛ]def</span><span id=test3>//test3</span><Br>
  <script>
   alert('START');
   alert('test1: ' + document.getElementById('test1').offsetLeft);
   alert('test2: ' + document.getElementById('test2').offsetLeft);
   alert('test3: ' + document.getElementById('test3').offsetLeft);
  </script>
 </body>
</html>
Неотображаемый символ вы должны будете сами поставить в текстовом редакторе, пусть это будет для примера пробел нулевой ширины (ZWSP или ZERO-WIDTH SPACE, код 8203, U+200B), потом надо будет сохранить код в html файл ОБЯЗАТЕЛЬНО В КОДИРОВКЕ UTF-8, иначе работать не будет.
Если вы откроете файл в браузере, будет выдано следующее: "test1: 51", "test2: 62", "test3: 51". Для test1 и test3 числовые значения будут равны, так как ОТОБРАЖАЕМЫЕ ДЛИНЫ ТЕКСТОВ, стоящих в span-ах перед ними равны, хоть сами тексты (последовательности символов) в них разные.

Если вы не хотите заморачиваться или для вас это слишком сложно , вот вам подготовленный мною альтернативный код:
HTML:
Развернуть Свернуть Копировать
<html>
 <head><meta charset="UTF-8"></head>
 <body>
  <span>abcdef</span><span id=test1>//test1</span><Br>
  <span>abcXdef</span><span id=test2>//test2</span><Br>
  <span>abc&#8203;def</span><span id=test3>//test3</span><Br>
  <script>
   alert('START');
   alert('test1: ' + document.getElementById('test1').offsetLeft);
   alert('test2: ' + document.getElementById('test2').offsetLeft);
   alert('test3: ' + document.getElementById('test3').offsetLeft);
  </script>
 </body>
</html>
Конструкция &#8203;создаёт символ с кодом 8203 (пробел нулевой ширины) между abc и def. Запустите этот код в браузере и посмотрите как он работает.

Думаю я смог на пальцах показать базовый принцип работы. Ну а теперь перейдём к коду уже более приближённому к боевому.
HTML:
Развернуть Свернуть Копировать
<html>
 <head><meta charset="UTF-8"></head>
 <body>
   <span>abcdef</span><span id="normal"></span><Br>

   <span>abc&#0;def</span><span id="code_0"> &lt;- char code 0</span><Br>
   <span>abc&#1;def</span><span id="code_1"> &lt;- char code 1</span><Br>
   <span>abc&#2;def</span><span id="code_2"> &lt;- char code 2</span><Br>
   <span>abc&#3;def</span><span id="code_3"> &lt;- char code 3</span><Br>
   <span>abc&#4;def</span><span id="code_4"> &lt;- char code 4</span><Br>
   <span>abc&#5;def</span><span id="code_5"> &lt;- char code 5</span><Br>
   <span>abc&#6;def</span><span id="code_6"> &lt;- char code 6</span><Br>
   <span>abc&#7;def</span><span id="code_7"> &lt;- char code 7</span><Br>
   <span>abc&#8;def</span><span id="code_8"> &lt;- char code 8</span><Br>
   <span>abc&#9;def</span><span id="code_9"> &lt;- char code 9</span><Br>
   <span>abc&#10;def</span><span id="code_10"> &lt;- char code 10</span><Br>
   <span>abc&#11;def</span><span id="code_11"> &lt;- char code 11</span><Br>
   <span>abc&#12;def</span><span id="code_12"> &lt;- char code 12</span><Br>
   <span>abc&#13;def</span><span id="code_13"> &lt;- char code 13</span><Br>
   <span>abc&#14;def</span><span id="code_14"> &lt;- char code 14</span><Br>
   <span>abc&#15;def</span><span id="code_15"> &lt;- char code 15</span><Br>
   <span>abc&#16;def</span><span id="code_16"> &lt;- char code 16</span><Br>
   <span>abc&#17;def</span><span id="code_17"> &lt;- char code 17</span><Br>
   <span>abc&#18;def</span><span id="code_18"> &lt;- char code 18</span><Br>
   <span>abc&#19;def</span><span id="code_19"> &lt;- char code 19</span><Br>
   <span>abc&#20;def</span><span id="code_20"> &lt;- char code 20</span><Br>
   <span>abc&#21;def</span><span id="code_21"> &lt;- char code 21</span><Br>
   <span>abc&#22;def</span><span id="code_22"> &lt;- char code 22</span><Br>
   <span>abc&#23;def</span><span id="code_23"> &lt;- char code 23</span><Br>
   <span>abc&#24;def</span><span id="code_24"> &lt;- char code 24</span><Br>
   <span>abc&#25;def</span><span id="code_25"> &lt;- char code 25</span><Br>
   <span>abc&#26;def</span><span id="code_26"> &lt;- char code 26</span><Br>
   <span>abc&#27;def</span><span id="code_27"> &lt;- char code 27</span><Br>
   <span>abc&#28;def</span><span id="code_28"> &lt;- char code 28</span><Br>
   <span>abc&#29;def</span><span id="code_29"> &lt;- char code 29</span><Br>
   <span>abc&#30;def</span><span id="code_30"> &lt;- char code 30</span><Br>
   <span>abc&#31;def</span><span id="code_31"> &lt;- char code 31</span><Br>
   <span>abc&#32;def</span><span id="code_32"> &lt;- char code 32</span><Br>
   <span>abc&#33;def</span><span id="code_33"> &lt;- char code 33</span><Br>
   <span>abc&#34;def</span><span id="code_34"> &lt;- char code 34</span><Br>
   <span>abc&#35;def</span><span id="code_35"> &lt;- char code 35</span><Br>
   <span>abc&#36;def</span><span id="code_36"> &lt;- char code 36</span><Br>
   <span>abc&#37;def</span><span id="code_37"> &lt;- char code 37</span><Br>
   <span>abc&#38;def</span><span id="code_38"> &lt;- char code 38</span><Br>
   <span>abc&#39;def</span><span id="code_39"> &lt;- char code 39</span><Br>
   <span>abc&#40;def</span><span id="code_40"> &lt;- char code 40</span><Br>
   <span>abc&#41;def</span><span id="code_41"> &lt;- char code 41</span><Br>
   <span>abc&#42;def</span><span id="code_42"> &lt;- char code 42</span><Br>
   <span>abc&#43;def</span><span id="code_43"> &lt;- char code 43</span><Br>
   <span>abc&#44;def</span><span id="code_44"> &lt;- char code 44</span><Br>
   <span>abc&#45;def</span><span id="code_45"> &lt;- char code 45</span><Br>
   <span>abc&#46;def</span><span id="code_46"> &lt;- char code 46</span><Br>
   <span>abc&#47;def</span><span id="code_47"> &lt;- char code 47</span><Br>
   <span>abc&#48;def</span><span id="code_48"> &lt;- char code 48</span><Br>
   <span>abc&#49;def</span><span id="code_49"> &lt;- char code 49</span><Br>
   <span>abc&#50;def</span><span id="code_50"> &lt;- char code 50</span><Br>
   <span>abc&#51;def</span><span id="code_51"> &lt;- char code 51</span><Br>
   <span>abc&#52;def</span><span id="code_52"> &lt;- char code 52</span><Br>
   <span>abc&#53;def</span><span id="code_53"> &lt;- char code 53</span><Br>
   <span>abc&#54;def</span><span id="code_54"> &lt;- char code 54</span><Br>
   <span>abc&#55;def</span><span id="code_55"> &lt;- char code 55</span><Br>
   <span>abc&#56;def</span><span id="code_56"> &lt;- char code 56</span><Br>
   <span>abc&#57;def</span><span id="code_57"> &lt;- char code 57</span><Br>
   <span>abc&#58;def</span><span id="code_58"> &lt;- char code 58</span><Br>
   <span>abc&#59;def</span><span id="code_59"> &lt;- char code 59</span><Br>
   <span>abc&#60;def</span><span id="code_60"> &lt;- char code 60</span><Br>
   <span>abc&#61;def</span><span id="code_61"> &lt;- char code 61</span><Br>
   <span>abc&#62;def</span><span id="code_62"> &lt;- char code 62</span><Br>
   <span>abc&#63;def</span><span id="code_63"> &lt;- char code 63</span><Br>
   <span>abc&#64;def</span><span id="code_64"> &lt;- char code 64</span><Br>
   <span>abc&#65;def</span><span id="code_65"> &lt;- char code 65</span><Br>
   <span>abc&#66;def</span><span id="code_66"> &lt;- char code 66</span><Br>
   <span>abc&#67;def</span><span id="code_67"> &lt;- char code 67</span><Br>
   <span>abc&#68;def</span><span id="code_68"> &lt;- char code 68</span><Br>
   <span>abc&#69;def</span><span id="code_69"> &lt;- char code 69</span><Br>
   <span>abc&#70;def</span><span id="code_70"> &lt;- char code 70</span><Br>
   <span>abc&#71;def</span><span id="code_71"> &lt;- char code 71</span><Br>
   <span>abc&#72;def</span><span id="code_72"> &lt;- char code 72</span><Br>
   <span>abc&#73;def</span><span id="code_73"> &lt;- char code 73</span><Br>
   <span>abc&#74;def</span><span id="code_74"> &lt;- char code 74</span><Br>
   <span>abc&#75;def</span><span id="code_75"> &lt;- char code 75</span><Br>
   <span>abc&#76;def</span><span id="code_76"> &lt;- char code 76</span><Br>
   <span>abc&#77;def</span><span id="code_77"> &lt;- char code 77</span><Br>
   <span>abc&#78;def</span><span id="code_78"> &lt;- char code 78</span><Br>
   <span>abc&#79;def</span><span id="code_79"> &lt;- char code 79</span><Br>
   <span>abc&#80;def</span><span id="code_80"> &lt;- char code 80</span><Br>
   <span>abc&#81;def</span><span id="code_81"> &lt;- char code 81</span><Br>
   <span>abc&#82;def</span><span id="code_82"> &lt;- char code 82</span><Br>
   <span>abc&#83;def</span><span id="code_83"> &lt;- char code 83</span><Br>
   <span>abc&#84;def</span><span id="code_84"> &lt;- char code 84</span><Br>
   <span>abc&#85;def</span><span id="code_85"> &lt;- char code 85</span><Br>
   <span>abc&#86;def</span><span id="code_86"> &lt;- char code 86</span><Br>
   <span>abc&#87;def</span><span id="code_87"> &lt;- char code 87</span><Br>
   <span>abc&#88;def</span><span id="code_88"> &lt;- char code 88</span><Br>
   <span>abc&#89;def</span><span id="code_89"> &lt;- char code 89</span><Br>
   <span>abc&#90;def</span><span id="code_90"> &lt;- char code 90</span><Br>
   <span>abc&#91;def</span><span id="code_91"> &lt;- char code 91</span><Br>
   <span>abc&#92;def</span><span id="code_92"> &lt;- char code 92</span><Br>
   <span>abc&#93;def</span><span id="code_93"> &lt;- char code 93</span><Br>
   <span>abc&#94;def</span><span id="code_94"> &lt;- char code 94</span><Br>
   <span>abc&#95;def</span><span id="code_95"> &lt;- char code 95</span><Br>
   <span>abc&#96;def</span><span id="code_96"> &lt;- char code 96</span><Br>
   <span>abc&#97;def</span><span id="code_97"> &lt;- char code 97</span><Br>
   <span>abc&#98;def</span><span id="code_98"> &lt;- char code 98</span><Br>
   <span>abc&#99;def</span><span id="code_99"> &lt;- char code 99</span><Br>
   <span>abc&#100;def</span><span id="code_100"> &lt;- char code 100</span><Br>
   <span>abc&#101;def</span><span id="code_101"> &lt;- char code 101</span><Br>
   <span>abc&#102;def</span><span id="code_102"> &lt;- char code 102</span><Br>
   <span>abc&#103;def</span><span id="code_103"> &lt;- char code 103</span><Br>
   <span>abc&#104;def</span><span id="code_104"> &lt;- char code 104</span><Br>
   <span>abc&#105;def</span><span id="code_105"> &lt;- char code 105</span><Br>
   <span>abc&#106;def</span><span id="code_106"> &lt;- char code 106</span><Br>
   <span>abc&#107;def</span><span id="code_107"> &lt;- char code 107</span><Br>
   <span>abc&#108;def</span><span id="code_108"> &lt;- char code 108</span><Br>
   <span>abc&#109;def</span><span id="code_109"> &lt;- char code 109</span><Br>
   <span>abc&#110;def</span><span id="code_110"> &lt;- char code 110</span><Br>
   <span>abc&#111;def</span><span id="code_111"> &lt;- char code 111</span><Br>
   <span>abc&#112;def</span><span id="code_112"> &lt;- char code 112</span><Br>
   <span>abc&#113;def</span><span id="code_113"> &lt;- char code 113</span><Br>
   <span>abc&#114;def</span><span id="code_114"> &lt;- char code 114</span><Br>
   <span>abc&#115;def</span><span id="code_115"> &lt;- char code 115</span><Br>
   <span>abc&#116;def</span><span id="code_116"> &lt;- char code 116</span><Br>
   <span>abc&#117;def</span><span id="code_117"> &lt;- char code 117</span><Br>
   <span>abc&#118;def</span><span id="code_118"> &lt;- char code 118</span><Br>
   <span>abc&#119;def</span><span id="code_119"> &lt;- char code 119</span><Br>
   <span>abc&#120;def</span><span id="code_120"> &lt;- char code 120</span><Br>
   <span>abc&#121;def</span><span id="code_121"> &lt;- char code 121</span><Br>
   <span>abc&#122;def</span><span id="code_122"> &lt;- char code 122</span><Br>
   <span>abc&#123;def</span><span id="code_123"> &lt;- char code 123</span><Br>
   <span>abc&#124;def</span><span id="code_124"> &lt;- char code 124</span><Br>
   <span>abc&#125;def</span><span id="code_125"> &lt;- char code 125</span><Br>
   <span>abc&#126;def</span><span id="code_126"> &lt;- char code 126</span><Br>
   <span>abc&#127;def</span><span id="code_127"> &lt;- char code 127</span><Br>
   <span>abc&#128;def</span><span id="code_128"> &lt;- char code 128</span><Br>
   <span>abc&#129;def</span><span id="code_129"> &lt;- char code 129</span><Br>
   <span>abc&#130;def</span><span id="code_130"> &lt;- char code 130</span><Br>
   <span>abc&#131;def</span><span id="code_131"> &lt;- char code 131</span><Br>
   <span>abc&#132;def</span><span id="code_132"> &lt;- char code 132</span><Br>
   <span>abc&#133;def</span><span id="code_133"> &lt;- char code 133</span><Br>
   <span>abc&#134;def</span><span id="code_134"> &lt;- char code 134</span><Br>
   <span>abc&#135;def</span><span id="code_135"> &lt;- char code 135</span><Br>
   <span>abc&#136;def</span><span id="code_136"> &lt;- char code 136</span><Br>
   <span>abc&#137;def</span><span id="code_137"> &lt;- char code 137</span><Br>
   <span>abc&#138;def</span><span id="code_138"> &lt;- char code 138</span><Br>
   <span>abc&#139;def</span><span id="code_139"> &lt;- char code 139</span><Br>
   <span>abc&#140;def</span><span id="code_140"> &lt;- char code 140</span><Br>
   <span>abc&#141;def</span><span id="code_141"> &lt;- char code 141</span><Br>
   <span>abc&#142;def</span><span id="code_142"> &lt;- char code 142</span><Br>
   <span>abc&#143;def</span><span id="code_143"> &lt;- char code 143</span><Br>
   <span>abc&#144;def</span><span id="code_144"> &lt;- char code 144</span><Br>
   <span>abc&#145;def</span><span id="code_145"> &lt;- char code 145</span><Br>
   <span>abc&#146;def</span><span id="code_146"> &lt;- char code 146</span><Br>
   <span>abc&#147;def</span><span id="code_147"> &lt;- char code 147</span><Br>
   <span>abc&#148;def</span><span id="code_148"> &lt;- char code 148</span><Br>
   <span>abc&#149;def</span><span id="code_149"> &lt;- char code 149</span><Br>
   <span>abc&#150;def</span><span id="code_150"> &lt;- char code 150</span><Br>
   <span>abc&#151;def</span><span id="code_151"> &lt;- char code 151</span><Br>
   <span>abc&#152;def</span><span id="code_152"> &lt;- char code 152</span><Br>
   <span>abc&#153;def</span><span id="code_153"> &lt;- char code 153</span><Br>
   <span>abc&#154;def</span><span id="code_154"> &lt;- char code 154</span><Br>
   <span>abc&#155;def</span><span id="code_155"> &lt;- char code 155</span><Br>
   <span>abc&#156;def</span><span id="code_156"> &lt;- char code 156</span><Br>
   <span>abc&#157;def</span><span id="code_157"> &lt;- char code 157</span><Br>
   <span>abc&#158;def</span><span id="code_158"> &lt;- char code 158</span><Br>
   <span>abc&#159;def</span><span id="code_159"> &lt;- char code 159</span><Br>
   <span>abc&#160;def</span><span id="code_160"> &lt;- char code 160</span><Br>
   <span>abc&#161;def</span><span id="code_161"> &lt;- char code 161</span><Br>
   <span>abc&#162;def</span><span id="code_162"> &lt;- char code 162</span><Br>
   <span>abc&#163;def</span><span id="code_163"> &lt;- char code 163</span><Br>
   <span>abc&#164;def</span><span id="code_164"> &lt;- char code 164</span><Br>
   <span>abc&#165;def</span><span id="code_165"> &lt;- char code 165</span><Br>
   <span>abc&#166;def</span><span id="code_166"> &lt;- char code 166</span><Br>
   <span>abc&#167;def</span><span id="code_167"> &lt;- char code 167</span><Br>
   <span>abc&#168;def</span><span id="code_168"> &lt;- char code 168</span><Br>
   <span>abc&#169;def</span><span id="code_169"> &lt;- char code 169</span><Br>
   <span>abc&#170;def</span><span id="code_170"> &lt;- char code 170</span><Br>
   <span>abc&#171;def</span><span id="code_171"> &lt;- char code 171</span><Br>
   <span>abc&#172;def</span><span id="code_172"> &lt;- char code 172</span><Br>
   <span>abc&#173;def</span><span id="code_173"> &lt;- char code 173</span><Br>
   <span>abc&#174;def</span><span id="code_174"> &lt;- char code 174</span><Br>
   <span>abc&#175;def</span><span id="code_175"> &lt;- char code 175</span><Br>
   <span>abc&#176;def</span><span id="code_176"> &lt;- char code 176</span><Br>
   <span>abc&#177;def</span><span id="code_177"> &lt;- char code 177</span><Br>
   <span>abc&#178;def</span><span id="code_178"> &lt;- char code 178</span><Br>
   <span>abc&#179;def</span><span id="code_179"> &lt;- char code 179</span><Br>
   <span>abc&#180;def</span><span id="code_180"> &lt;- char code 180</span><Br>
   <span>abc&#181;def</span><span id="code_181"> &lt;- char code 181</span><Br>
   <span>abc&#182;def</span><span id="code_182"> &lt;- char code 182</span><Br>
   <span>abc&#183;def</span><span id="code_183"> &lt;- char code 183</span><Br>
   <span>abc&#184;def</span><span id="code_184"> &lt;- char code 184</span><Br>
   <span>abc&#185;def</span><span id="code_185"> &lt;- char code 185</span><Br>
   <span>abc&#186;def</span><span id="code_186"> &lt;- char code 186</span><Br>
   <span>abc&#187;def</span><span id="code_187"> &lt;- char code 187</span><Br>
   <span>abc&#188;def</span><span id="code_188"> &lt;- char code 188</span><Br>
   <span>abc&#189;def</span><span id="code_189"> &lt;- char code 189</span><Br>
   <span>abc&#190;def</span><span id="code_190"> &lt;- char code 190</span><Br>
   <span>abc&#191;def</span><span id="code_191"> &lt;- char code 191</span><Br>
   <span>abc&#192;def</span><span id="code_192"> &lt;- char code 192</span><Br>
   <span>abc&#193;def</span><span id="code_193"> &lt;- char code 193</span><Br>
   <span>abc&#194;def</span><span id="code_194"> &lt;- char code 194</span><Br>
   <span>abc&#195;def</span><span id="code_195"> &lt;- char code 195</span><Br>
   <span>abc&#196;def</span><span id="code_196"> &lt;- char code 196</span><Br>
   <span>abc&#197;def</span><span id="code_197"> &lt;- char code 197</span><Br>
   <span>abc&#198;def</span><span id="code_198"> &lt;- char code 198</span><Br>
   <span>abc&#199;def</span><span id="code_199"> &lt;- char code 199</span><Br>
   <span>abc&#200;def</span><span id="code_200"> &lt;- char code 200</span><Br>
   <span>abc&#201;def</span><span id="code_201"> &lt;- char code 201</span><Br>
   <span>abc&#202;def</span><span id="code_202"> &lt;- char code 202</span><Br>
   <span>abc&#203;def</span><span id="code_203"> &lt;- char code 203</span><Br>
   <span>abc&#204;def</span><span id="code_204"> &lt;- char code 204</span><Br>
   <span>abc&#205;def</span><span id="code_205"> &lt;- char code 205</span><Br>
   <span>abc&#206;def</span><span id="code_206"> &lt;- char code 206</span><Br>
   <span>abc&#207;def</span><span id="code_207"> &lt;- char code 207</span><Br>
   <span>abc&#208;def</span><span id="code_208"> &lt;- char code 208</span><Br>
   <span>abc&#209;def</span><span id="code_209"> &lt;- char code 209</span><Br>
   <span>abc&#210;def</span><span id="code_210"> &lt;- char code 210</span><Br>
   <span>abc&#211;def</span><span id="code_211"> &lt;- char code 211</span><Br>
   <span>abc&#212;def</span><span id="code_212"> &lt;- char code 212</span><Br>
   <span>abc&#213;def</span><span id="code_213"> &lt;- char code 213</span><Br>
   <span>abc&#214;def</span><span id="code_214"> &lt;- char code 214</span><Br>
   <span>abc&#215;def</span><span id="code_215"> &lt;- char code 215</span><Br>
   <span>abc&#216;def</span><span id="code_216"> &lt;- char code 216</span><Br>
   <span>abc&#217;def</span><span id="code_217"> &lt;- char code 217</span><Br>
   <span>abc&#218;def</span><span id="code_218"> &lt;- char code 218</span><Br>
   <span>abc&#219;def</span><span id="code_219"> &lt;- char code 219</span><Br>
   <span>abc&#220;def</span><span id="code_220"> &lt;- char code 220</span><Br>
   <span>abc&#221;def</span><span id="code_221"> &lt;- char code 221</span><Br>
   <span>abc&#222;def</span><span id="code_222"> &lt;- char code 222</span><Br>
   <span>abc&#223;def</span><span id="code_223"> &lt;- char code 223</span><Br>
   <span>abc&#224;def</span><span id="code_224"> &lt;- char code 224</span><Br>
   <span>abc&#225;def</span><span id="code_225"> &lt;- char code 225</span><Br>
   <span>abc&#226;def</span><span id="code_226"> &lt;- char code 226</span><Br>
   <span>abc&#227;def</span><span id="code_227"> &lt;- char code 227</span><Br>
   <span>abc&#228;def</span><span id="code_228"> &lt;- char code 228</span><Br>
   <span>abc&#229;def</span><span id="code_229"> &lt;- char code 229</span><Br>
   <span>abc&#230;def</span><span id="code_230"> &lt;- char code 230</span><Br>
   <span>abc&#231;def</span><span id="code_231"> &lt;- char code 231</span><Br>
   <span>abc&#232;def</span><span id="code_232"> &lt;- char code 232</span><Br>
   <span>abc&#233;def</span><span id="code_233"> &lt;- char code 233</span><Br>
   <span>abc&#234;def</span><span id="code_234"> &lt;- char code 234</span><Br>
   <span>abc&#235;def</span><span id="code_235"> &lt;- char code 235</span><Br>
   <span>abc&#236;def</span><span id="code_236"> &lt;- char code 236</span><Br>
   <span>abc&#237;def</span><span id="code_237"> &lt;- char code 237</span><Br>
   <span>abc&#238;def</span><span id="code_238"> &lt;- char code 238</span><Br>
   <span>abc&#239;def</span><span id="code_239"> &lt;- char code 239</span><Br>
   <span>abc&#240;def</span><span id="code_240"> &lt;- char code 240</span><Br>
   <span>abc&#241;def</span><span id="code_241"> &lt;- char code 241</span><Br>
   <span>abc&#242;def</span><span id="code_242"> &lt;- char code 242</span><Br>
   <span>abc&#243;def</span><span id="code_243"> &lt;- char code 243</span><Br>
   <span>abc&#244;def</span><span id="code_244"> &lt;- char code 244</span><Br>
   <span>abc&#245;def</span><span id="code_245"> &lt;- char code 245</span><Br>
   <span>abc&#246;def</span><span id="code_246"> &lt;- char code 246</span><Br>
   <span>abc&#247;def</span><span id="code_247"> &lt;- char code 247</span><Br>
   <span>abc&#248;def</span><span id="code_248"> &lt;- char code 248</span><Br>
   <span>abc&#249;def</span><span id="code_249"> &lt;- char code 249</span><Br>
   <span>abc&#250;def</span><span id="code_250"> &lt;- char code 250</span><Br>
   <span>abc&#251;def</span><span id="code_251"> &lt;- char code 251</span><Br>
   <span>abc&#252;def</span><span id="code_252"> &lt;- char code 252</span><Br>
   <span>abc&#253;def</span><span id="code_253"> &lt;- char code 253</span><Br>
   <span>abc&#254;def</span><span id="code_254"> &lt;- char code 254</span><Br>
   <span>abc&#255;def</span><span id="code_255"> &lt;- char code 255</span><Br>
 

   <script>
    alert('START');
    normalOffset = document.getElementById('normal').offsetLeft;
 
    for (code = 0;code < 256;++code)
     if (document.getElementById('code_' + code.toString()).offsetLeft == normalOffset)
      alert('Void char code: ' + code.toString());

   </script>
 </body>
</html>
В самом начале присутствует следующий кусок html кода:
<span>abcdef</span><span id="normal"></span><Br>
А внизу есть строчка javascript кода:
normalOffset = document.getElementById('normal').offsetLeft;
Так мы получаем эталонное значение смещения, с которым мы будем сравнивать смещения span-ов с id для выявления неотображаемых символов.
Далее идёт куча кусков html кода формата:
<span>abc&#[КОД ЮНИКОД СИМВОЛА];def</span><span id="code_[КОД ЭТОГО ЮНИКОД СИМВОЛА]"> &lt;- char code [КОД ЭТОГО ЮНИКОД СИМВОЛА]</span><Br>
Текст внутри span с id сгенерирован только для наглядности и мог бы вообще отсутствовать:
<span>abc&#[КОД ЮНИКОД СИМВОЛА];def</span><span id="code_[КОД ЭТОГО ЮНИКОД СИМВОЛА]"></span><Br>
Если бы мы хотели проверить, отображается ли символ с определённым кодом (пусть это будет 111), мы бы могли просто получить левое смещение span c id "code_111" и сравнить его с эталонным:
JavaScript:
Развернуть Свернуть Копировать
if (document.getElementById('code_111').offsetLeft != normalOffset)
  alert('Yes');
else alert('No');

Сам файл сгенерирован для юникод символов с кодами от 0 до 255. Внизу есть javascript код, который осуществляет проверку для каждого:
JavaScript:
Развернуть Свернуть Копировать
for (code = 0;code < 256;++code)
 if (document.getElementById('code_' + code.toString()).offsetLeft == normalOffset)
  alert('Void char code: ' + code.toString());
Получается что-то вроде массовой автоматизированной проверки . Если вы сохраните весь код в файл и откроете этот файл в гугл хроме, то будет найден всего один символ с кодом 173 (но это только первые 256 символов юникода из более чем миллиона). В фаерфоксе будут найдены десятки. Разное ПО может работать совершенно по-разному. Но в хроме сидит абсолютное большинство пользователей так что нам интересен только он.

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

Вообще по началу я всё это реализовывал в html файлах просто открывая их в обычном браузере. У меня есть интерактивный html файл-программа в котором в браузере можно задавать разные настройки и делать всякие штуки, в том числе вышеописанные. Если будет кому-то интересно, могу его причесать и выложить. Но как оказалось браузеры, html и javascript созданы немного для других задач, а не для чека сотен тысяч юникод символов. Даже если реализовывать проверки по частям (а по другому в принципе не получится), генерируя куски html кодов и устанавливая их через innerHTML и проводя очередную проверку, то даже в этом случае браузер очень быстро уйдёт в нокдаун и начнут постоянно вылезать окна с предложением завершить скрипт на которые придётся постоянно кликать. Идеально здесь работает перезагрузка страницы после проверки очередной партии, но с локальными файлами так не получится. Можно заморочиться, взять хостинг, поставить на него скрипты, которые будут генерировать проверочные файлы по GET параметрам и проходить по ним тем же зеннопостером, но можно всё это реализовать полностью внутри самого зеннопостер. Что я собственно и сделал и получил стабильную самодостаточную программу с красивыми входными настойками работающую без глюков и тормозов и сохраняющую полученный результат прямо в файл, работая с которой ты ощущаешь себя полноценным белым человеком.

Подводные камни.
Существуют диакритические юникод символы. Наглядный пример такого: "c" + " ̈ " = "c̈" . Присутствуя в юникод тексте они обычно не меняют его отображаемую длину, но они меняют визуальное отображение стоящих перед ними символов и всего текста в целом, и такие символы нам неинтересны. Есть ещё похожие на них символы зачёркивания, подчёркивания, перечёркивания и т. д. Коды таких символов располагаются в узких числовых диапазонах. И при финальной ручной допроверке полученных результатов такие символы легко отбрасываются. К тому же второй способ такие символы не пропускает.

Второй сособ​

Этот способ не столь очевиден, но для меня он оказался ещё удобнее. Рaссмотрим следующий html код:
HTML:
Развернуть Свернуть Копировать
<html>
 <head><meta charset="UTF-8"></head>
 <body>
  <a id="code_8203" href="https://www.goo&#8203;gle.com/">link</a>
 </body>
</html>

Конструкция &#8203; проставляет символ с кодом 8203 между строками https://www.goo и gle.com/ . При этом если вы откроете этот код в браузере и нажмёте на ссылку то перейдёте на https://www.google.com/ . Как оказалось браузер особенным образом обрабатывает значения атрибутов href у ссылок: если в доменах урлов присутствуют определённые символы, то эти символы вырезаются из значений без следа. И как оказалось множество этих символов почти на 100% совпадает с множеством неотображаемых в html тексте символов.

Можно добавить javascript код и проверить значение атрибута href у ссылки:
HTML:
Развернуть Свернуть Копировать
<html>
 <head><meta charset="UTF-8"></head>
 <body>
  <a id="code_8203" href="https://www.goo&#8203;gle.com/">link</a>
  <script>
   if (document.getElementById('code_8203').href == 'https://www.google.com/')
    alert('EQUAL');
   else alert('NOT EQUAL');
  </script>
 </body>
</html>

Если мы хотим проверить этим принципом символ на неотображаемость, пусть для определённости это будет символ с числовым кодом 111. То мы можем сгенерировать следующий html код:
HTML:
Развернуть Свернуть Копировать
<a id="code_111" href="https://www.goo&#111;gle.com/">link</a>
И саму проверку произвести следующим javascript кодом:
JavaScript:
Развернуть Свернуть Копировать
if (document.getElementById('code_111').href == 'https://www.google.com/')
  alert('YES');
 else alert('NO');
Далее я уже подробнее не буду расписывать. Всё по аналогии с первым методом, только проверять для каждого символа мы будем не смещение, а значение атрибута href. Этот метод даёт ложноположительные срабатывания для некоторых пробельных символов, но таких всего несколько, зато не срабатывает для диакритических символов.

Реализация шаблона​

Теперь давайте рассмотрим как всё вышеописанное реализовано в автономном ZennoPoster шаблоне.

Подготовка​

В шаблоне поиска невидимых юникод символов имеются пользовательские настройки. Главные из них: тип поиска, количество проверяемых кодов символов за раз и сами коды проверяемых символов. Коды проверяемых символов задаются в текстовом поле в формате: с новой строки либо код, либо диапазон кодов. Наглядный пример:
Код:
Развернуть Свернуть Копировать
1
8
12
128-196
199
1024-4096
Можете вбить 0-1114111 для проверки всего юникода. Этот текст сохраняется в переменную ZennoPoster "ranges".

После запуска шаблона первым делом происходит проверка входных параметров и преобразование переменной "ranges". По содержимому этой переменной в C# создаётся числовой массив, далее он сортируется, удаляются дубли и этот массив вновь кодируется в текст такого же формата. Если в массиве есть подряд идущие числа, то они кодируются в диапазон. Например, если вы вобьёте такие коды:
Код:
Развернуть Свернуть Копировать
7
1
3
9
2
2
5-6
То переменная ranges будет содержать после обработки такой текст:
Код:
Развернуть Свернуть Копировать
1-3
5-7
9
Это нужно для передачи массива всех кодов в javascript кубик, в котором и осуществляется проверка кодов на каждой новой итерации проверок. Переменные ZennoPoster "fromCodesIndex" и "boundaryCodesIndex" будут содержать нижний и верхний индекс массива кодов для которых будет осуществлена проверка при текущей итерации ("boundaryCodesIndex" не включительно).

Проверка​

Перед началом проверки очередной партии кодов происходит переход на url
data:text/html;charset=UTF-8,<html><head><title>Testing unicode</title></head><body><div id=codes></div></body></html>
Здесь задаётся простейшая html разметка.

Сама проверка полностью реализована в javascript кубике. Сперва в javascript из текста переменной "ranges" и значений переменных "fromCodesIndex", "boundaryCodesIndex" создаётся массив с кодами для текущей проверки:
JavaScript:
Развернуть Свернуть Копировать
//Получаем массив кодов для текущей проверки
var re = RegExp('(\\d+)-(\\d+)|(\\d+)', 'i')

var codes = []
for (var string of '{-Variable.ranges-}'.split("\n")) {
 var res = string.match(re)
 if (res[3] !== undefined)
  codes.push(parseInt(res[3]))азж
 else
  for (i = parseInt(res[1]), max = parseInt(res[2]);i <= max;++i)
   codes.push(i)
}
codes = codes.slice({-Variable.fromCodesIndex-}, {-Variable.boundaryCodesIndex-})
Далее по этим кодам символов генерируется специальный проверочный кусок html. Далее в div с id "codes" будет задан сгенерированный проверочный html код.
document.getElementById('codes').innerHTML = html
Далее произойдёт проверка для каждого кода. Я уже не буду расписывать, что мы генерируем и как проверяем. Всё это было подробно разжёвано в первой части статьи.

На следующей итерации вновь происходит переход на url
data:text/html;charset=UTF-8,<html><head><title>Testing unicode</title></head><body><div id=codes></div></body></html>
, то есть страница перезагружается, а значения переменных ZennoPoster "fromCodesIndex" и "boundaryCodesIndex" будут увеличены на величину шага, которую мы задаём в настройках. Если этого не делать, то, браузер упадёт.

Сохранение результатов​

В процессе проверок искомые коды будут сохраняться в javascript коде в массиве ok. Однако передать javascript массив в ZennoPoster мы не можем, поэтому приходится делать следующее. Кодировать массив ok в текстовый формат, подобно тому который описывался выше. Код под спойлером.
JavaScript:
Развернуть Свернуть Копировать
ranges = [];
for (i = 0, length = ok.length;i < length;) {
 var from = ok[i];
 for (++i, value = from;i < length && ok[i] == ++value; ++i);
 if (from === ok[i-1])
  ranges.push(ok[i-1].toString())
 else
  ranges.push(from.toString() + '-' + ok[i-1].toString())
}

return ranges.join(';');
Во время работы шаблона javascript кубик возвращает найденные коды в текстовом виде в ZennoPoster переменную "result". А содержимое переменной result добавляется к содержимому переменной totalResult. По окончанию работы все собранные коды сохраняются в файл ranges.txt в уже упомянутом формате: с новой строки либо единичный код, либо диапазон кодов.

Результаты и перепроверка​

При проверке первым методом всего юникода в браузере хром у меня получаются следующие коды:
Код:
Развернуть Свернуть Копировать
0
173
734
768 - 879
1155 - 1161
1425 - 1469
1471
1473 - 1474
1476 - 1477
1479
1541
1552 - 1562
1564
1611 - 1631
1648
1652
1750 - 1756
1759 - 1768
1770 - 1773
1807
1809
1840 - 1866
1958 - 1968
2260 - 2273
2275 - 2303
3633
3636 - 3642
3655 - 3662
3761
3764 - 3769
3771 - 3772
3784 - 3789
4957 - 4959
6068 - 6069
6089 - 6090
6092
6155 - 6158
7616 - 7626
7678 - 7679
8203 - 8207
8234 - 8237
8288 - 8303
8432
9185
11503 - 11505
11647
11744 - 11775
11791 - 11793
42607
42609 - 42610
42612 - 42621
42654 - 42655
42775 - 42778
42784 - 42785
43258
62209
64286
64435
64437
64439
64441
64443
64446
64606 - 64611
65024 - 65039
65056 - 65059
65279
65520 - 65528
68159
69939
119155 - 119162
917504 - 921599

При проверке вторым:
Код:
Развернуть Свернуть Копировать
9 - 10
13
173
847
6155 - 6157
8203 - 8205
8288
8292
65024 - 65039
65279
113824 - 113827
917760 - 917999

В обоих случаях есть ложные срабатывания. Во втором намного меньше.
Можно зайти на https://unicodeplus.com/ и вбить в поиске &#КОД СИМВОЛА; , откроется страница, на которой будет подробная информация об этом символе. Можно будет посмотреть как он выглядит или убедиться что он никак не отображается.

У меня есть html файл, в котором в текстовом поле можно вбивать диапазоны кодов символов и проверять, как эти символы отображаются в браузере.
Скриншот под спойлером:
fscr.png
Выложу если кому-то будет интересно.

Заключение​

ZennoPoster - это мощный инструмент автоматизации действий в браузере, даже не привязанных к конкретным сайтам.

Шаблон прикреплён.
 

Вложения

Последнее редактирование:

Кто просматривает тему: (Всего: 1, Пользователи: 1, Гости: 0)