Saltar al contenido

Filtrado utf8 no válido

Recabamos en todo el mundo on line para así tener para ti la solución a tu duda, si continúas con alguna inquietud puedes dejarnos tu duda y responderemos sin falta.

Solución:

Si quieres usar grep, tu puedes hacer:

grep -axv '.*' file

en configuraciones regionales UTF-8 para obtener las líneas que tienen al menos una secuencia UTF-8 no válida (esto funciona con GNU Grep al menos).

Creo que probablemente quieras iconv. Es para convertir entre conjuntos de códigos y admite una cantidad absurda de formatos. Por ejemplo, para eliminar cualquier cosa que no sea válida en UTF-8, puede usar:

iconv -c -t UTF-8 < input.txt > output.txt

Sin la opción -c, informará problemas al convertir a stderr, por lo que con la dirección del proceso podría guardar una lista de estos. Otra forma sería quitar las cosas que no son UTF8 y luego

diff input.txt output.txt

para obtener una lista de dónde se realizaron los cambios.

Editar: He corregido un error tipográfico en la expresión regular. Necesitaba un ‘ x80` no 80.

La expresión regular para filtrar formularios UTF-8 no válidos, por estricto la adherencia a UTF-8, es la siguiente

perl -l -ne '/
 ^( ([x00-x7F])              # 1-byte pattern
   |([xC2-xDF][x80-xBF])   # 2-byte pattern
   |((([xE0][xA0-xBF])|([xED][x80-x9F])|([xE1-xECxEE-xEF][x80-xBF]))([x80-xBF])) # 3-byte pattern
   |((([xF0][x90-xBF])|([xF1-xF3][x80-xBF])|([xF4][x80-x8F]))([x80-xBF]2))       # 4-byte pattern
  )*$ /x or print'

Salida (de líneas clave. De Prueba 1):

Codepoint
=========  
00001000  Test=1 mode=strict               valid,invalid,fail=(1000,0,0)          
0000E000  Test=1 mode=strict               valid,invalid,fail=(D800,800,0)          
0010FFFF  mode=strict  test-return=(0,0)   valid,invalid,fail=(10F800,800,0)          

P. ¿Cómo se crean datos de prueba para probar una expresión regular que filtra Unicode no válido?
A. Cree su propio algoritmo de prueba UTF-8 y rompa sus reglas …
22 capturas.. Pero entonces, ¿cómo prueba su algoritmo de prueba?

La expresión regular, arriba, ha sido probada (usando iconv como referencia) para cada valor entero de 0x00000 para 0x10FFFF.. Este valor superior es el valor entero máximo de un punto de código Unicode

  • En noviembre de 2003, RFC 3629 restringió UTF-8 a cuatro bytes que cubren solo el rango U + 0000 a U + 10FFFF, para que coincida con las restricciones de la codificación de caracteres UTF-16.

Según esta página de wikipedia UTF-8,.

  • UTF-8 codifica cada uno de los 1,112,064 puntos de código en el juego de caracteres Unicode, usando de uno a cuatro bytes de 8 bits

Este numeber (1,112,064) equivale a un rango 0x000000 para 0x10F7FF, que es 0x0800 por debajo del valor entero máximo real para el punto de código Unicode más alto: 0x10FFFF

Esta bloque de enteros falta en el espectro de puntos de código Unicode, debido a la necesidad de codificación UTF-16 para dar un paso más allá de su intención de diseño original a través de un sistema llamado pares sustitutos. Un bloque de 0x0800 enteros se ha reservado para ser utilizado por UTF-16 .. Este bloque abarca el rango0x00D800 para 0x00DFFF. Ninguno de estos números enteros son valores Unicode legales y, por lo tanto, son valores UTF-8 no válidos.

En Prueba 1, los regex ha sido probado contra todos los números en el rango de puntos de código Unicode, y coincide exactamente con los resultados de iconv .. es decir. 0x010F7FF valores válidos, y 0x000800 valores inválidos.

Sin embargo, ahora surge el problema de: * ¿Cómo maneja la expresión regular el valor UTF-8 fuera de rango? encima 0x010FFFF (UTF-8 puede extenderse a 6 bytes, con un valor entero máximo de 0x7FFFFFFF?
Para generar los necesarios *valores de bytes UTF-8 no Unicode, He usado el siguiente comando:

  perl -C -e 'print chr 0x'$hexUTF32BE

Para probar su validez (de alguna manera), he usado Gilles' UTF-8 regex …

  perl -l -ne '/
   ^( [00-177]                 # 1-byte pattern
     |[300-337][200-277]      # 2-byte pattern
     |[340-357][200-277]2   # 3-byte pattern
     |[360-367][200-277]3   # 4-byte pattern
     |[370-373][200-277]4   # 5-byte pattern
     |[374-375][200-277]5   # 6-byte pattern
    )*$ /x or print'

La salida de ‘perl’s print chr’ coincide con el filtrado de la expresión regular de Gilles. Una refuerza la validez de la otra. No puedo usar iconv porque solo maneja el subconjunto del estándar Unicode válido del estándar UTF-8 más amplio (original) …

Los nunbers involucrados son bastante grandes, por lo que he probado el tope de rango, el fondo de rango y varios escaneos escalonados en incrementos como, 11111, 13579, 33333, 53441 … Todos los resultados coinciden, así que ahora todo lo que queda es probar la expresión regular contra estos valores de estilo UTF-8 fuera de rango (no válidos para Unicode y, por lo tanto, también no son válidos para el propio UTF-8 estricto).


Aquí están los módulos de prueba:

[[ "$(locale charmap)" != "UTF-8" ]] &&  echo "ERROR: locale must be UTF-8, but it is $(locale charmap)"; exit 1; 

# Testing the UTF-8 regex
#
# Tests to check that the observed byte-ranges (above) have
#  been  accurately observed and included in the test code and final regex. 
# =========================================================================
: 2 bytes; B2=0 #  run-test=1   do-not-test=0
: 3 bytes; B3=0 #  run-test=1   do-not-test=0
: 4 bytes; B4=0 #  run-test=1   do-not-test=0 

:   regex; Rx=1 #  run-test=1   do-not-test=0

           ((strict=16)); mode[$strict]=strict # iconv -f UTF-16BE  then iconv -f UTF-32BE beyond 0xFFFF)
           ((   lax=32)); mode[$lax]=lax       # iconv -f UTF-32BE  only)

          # modebits=$strict
                  # UTF-8, in relation to UTF-16 has invalid values
                  # modebits=$strict automatically shifts to modebits=$lax
                  # when the tested integer exceeds 0xFFFF
          # modebits=$lax 
                  # UTF-8, in relation to UTF-32, has no restrictione


           # Test 1 Sequentially tests a range of Big-Endian integers
           #      * Unicode Codepoints are a subset ofBig-Endian integers            
           #        ( based on 'iconv' -f UTF-32BE -f UTF-8 )    
           # Note: strict UTF-8 has a few quirks because of UTF-16
                    #    Set modebits=16 to "strictly" test the low range

             Test=1; modebits=$strict
           # Test=2; modebits=$lax
           # Test=3
              mode3wlo=$(( 1*4)) # minimum chars * 4 ( '4' is for UTF-32BE )
              mode3whi=$((10*4)) # minimum chars * 4 ( '4' is for UTF-32BE )


#########################################################################  

# 1 byte  UTF-8 values: Nothing to do; no complexities.

#########################################################################

#  2 Byte  UTF-8 values:  Verifying that I've got the right range values.
if ((B2==1)) ; then  
  echo "# Test 2 bytes for Valid UTF-8 values: ie. values which are in range"
  # =========================================================================
  time 
  for d1 in 194..223 ;do
      #     bin       oct  hex  dec
      # lo  11000010  302   C2  194
      # hi  11011111  337   DF  223
      B2b1=$(printf "%0.2X" $d1)
      #
      for d2 in 128..191 ;do
          #     bin       oct  hex  dec
          # lo  10000000  200   80  128
          # hi  10111111  277   BF  191
          B2b2=$(printf "%0.2X" $d2)
          #
          echo -n "$B2b1$B2b2" |
            xxd -p -u -r  |
              iconv -f UTF-8 >/dev/null ||  
                echo "ERROR: Invalid UTF-8 found: $B2b1$B2b2"; exit 20; 
          #
      done
  done
  echo

  # Now do a negated test.. This takes longer, because there are more values.
  echo "# Test 2 bytes for Invalid values: ie. values which are out of range"
  # =========================================================================
  # Note: 'iconv' will treat a leading  x00-x7F as a valid leading single,
  #   so this negated test primes the first UTF-8 byte with values starting at x80
  time 
  for d1 in 128..193 224..255 ;do 
 #for d1 in 128..194 224..255 ;do # force a valid UTF-8 (needs $B2b2) 
      B2b1=$(printf "%0.2X" $d1)
      #
      for d2 in 0..127 192..255 ;do
     #for d2 in 0..128 192..255 ;do # force a valid UTF-8 (needs $B2b1)
          B2b2=$(printf "%0.2X" $d2)
          #
          echo -n "$B2b1$B2b2" |
            xxd -p -u -r |
              iconv -f UTF-8 2>/dev/null &&  
                echo "ERROR: VALID UTF-8 found: $B2b1$B2b2"; exit 21; 
          #
      done
  done
  echo
fi

#########################################################################

#  3 Byte  UTF-8 values:  Verifying that I've got the right range values.
if ((B3==1)) ; then  
  echo "# Test 3 bytes for Valid UTF-8 values: ie. values which are in range"
  # ========================================================================
  time 
  for d1 in 224..239 ;do
      #     bin       oct  hex  dec
      # lo  11100000  340   E0  224
      # hi  11101111  357   EF  239
      B3b1=$(printf "%0.2X" $d1)
      #
      if   [[ $B3b1 == "E0" ]] ; then
          B3b2range="$(echo 160..191)"
          #     bin       oct  hex  dec  
          # lo  10100000  240   A0  160  
          # hi  10111111  277   BF  191
      elif [[ $B3b1 == "ED" ]] ; then
          B3b2range="$(echo 128..159)"
          #     bin       oct  hex  dec  
          # lo  10000000  200   80  128  
          # hi  10011111  237   9F  159
      else
          B3b2range="$(echo 128..191)"
          #     bin       oct  hex  dec
          # lo  10000000  200   80  128
          # hi  10111111  277   BF  191
      fi
      # 
      for d2 in $B3b2range ;do
          B3b2=$(printf "%0.2X" $d2)
          echo "$B3b1 $B3b2 xx"
          #
          for d3 in 128..191 ;do
              #     bin       oct  hex  dec
              # lo  10000000  200   80  128
              # hi  10111111  277   BF  191
              B3b3=$(printf "%0.2X" $d3)
              #
              echo -n "$B3b1$B3b2$B3b3" |
                xxd -p -u -r  |
                  iconv -f UTF-8 >/dev/null ||  
                    echo "ERROR: Invalid UTF-8 found: $B3b1$B3b2$B3b3"; exit 30; 
              #
          done
      done
  done
  echo

  # Now do a negated test.. This takes longer, because there are more values.
  echo "# Test 3 bytes for Invalid values: ie. values which are out of range"
  # =========================================================================
  # Note: 'iconv' will treat a leading  x00-x7F as a valid leading single,
  #   so this negated test primes the first UTF-8 byte with values starting at x80
  #
  # real     26m28.462s  
  # user     27m12.526s  | stepping by 2
  # sys      13m11.193s /
  #
  # real    239m00.836s 
  # user    225m11.108s  | stepping by 1
  # sys     120m00.538s /
  #
  time 
  for d1 in 128..223..1 240..255..1 ;do 
 #for d1 in 128..224..1 239..255..1 ;do # force a valid UTF-8 (needs $B2b2,$B3b3) 
      B3b1=$(printf "%0.2X" $d1)
      #
      if   [[ $B3b1 == "E0" ]] ; then
          B3b2range="$(echo 0..159..1 192..255..1)"
         #B3b2range="$(> 192..255..1)" # force a valid UTF-8 (needs $B3b1,$B3b3)
      elif [[ $B3b1 == "ED" ]] ; then
          B3b2range="$(echo 0..127..1 160..255..1)"
         #B3b2range="$(echo 0..128..1 160..255..1)" # force a valid UTF-8 (needs $B3b1,$B3b3)
      else
          B3b2range="$(echo 0..127..1 192..255..1)"
         #B3b2range="$(echo 0..128..1 192..255..1)" # force a valid UTF-8 (needs $B3b1,$B3b3)
      fi
      for d2 in $B3b2range ;do
          B3b2=$(printf "%0.2X" $d2)
          echo "$B3b1 $B3b2 xx"
          #
          for d3 in 0..127..1 192..255..1 ;do
         #for d3 in 0..128..1 192..255..1 ;do # force a valid UTF-8 (needs $B2b1)
              B3b3=$(printf "%0.2X" $d3)
              #
              echo -n "$B3b1$B3b2$B3b3" |
                xxd -p -u -r |
                  iconv -f UTF-8 2>/dev/null &&  
                    echo "ERROR: VALID UTF-8 found: $B3b1$B3b2$B3b3"; exit 31; 
              #
          done
      done
  done
  echo

fi

#########################################################################

#  Brute force testing in the Astral Plane will take a VERY LONG time..
#  Perhaps selective testing is more appropriate, now that the previous tests 
#     have panned out okay... 
#  
#  4 Byte  UTF-8 values:
if ((B4==1)) ; then  
  echo "# Test 4 bytes for Valid UTF-8 values: ie. values which are in range"
  # ==================================================================
  # real    58m18.531s 
  # user    56m44.317s  | 
  # sys     27m29.867s /
  time 
  for d1 in 240..244 ;do
      #     bin       oct  hex  dec
      # lo  11110000  360   F0  240
      # hi  11110100  364   F4  244  -- F4 encodes some values greater than 0x10FFFF;
      #                                    such a sequence is invalid.
      B4b1=$(printf "%0.2X" $d1)
      #
      if   [[ $B4b1 == "F0" ]] ; then
        B4b2range="$(echo 144..191)" ## f0 90 80 80  to  f0 bf bf bf
        #     bin       oct  hex  dec          010000  --  03FFFF 
        # lo  10010000  220   90  144  
        # hi  10111111  277   BF  191
        #                            
      elif [[ $B4b1 == "F4" ]] ; then
        B4b2range="$(echo 128..143)" ## f4 80 80 80  to  f4 8f bf bf
        #     bin       oct  hex  dec          100000  --  10FFFF 
        # lo  10000000  200   80  128  
        # hi  10001111  217   8F  143  -- F4 encodes some values greater than 0x10FFFF;
        #                                    such a sequence is invalid.
      else
        B4b2range="$(echo 128..191)" ## fx 80 80 80  to  f3 bf bf bf
        #     bin       oct  hex  dec          0C0000  --  0FFFFF
        # lo  10000000  200   80  128          0A0000
        # hi  10111111  277   BF  191
      fi
      #
      for d2 in $B4b2range ;do
          B4b2=$(printf "%0.2X" $d2)
          #
          for d3 in 128..191 ;do
              #     bin       oct  hex  dec
              # lo  10000000  200   80  128
              # hi  10111111  277   BF  191
              B4b3=$(printf "%0.2X" $d3)
              echo "$B4b1 $B4b2 $B4b3 xx"
              #
              for d4 in 128..191 ;do
                  #     bin       oct  hex  dec
                  # lo  10000000  200   80  128
                  # hi  10111111  277   BF  191
                  B4b4=$(printf "%0.2X" $d4)
                  #
                  echo -n "$B4b1$B4b2$B4b3$B4b4" |
                    xxd -p -u -r  |
                      iconv -f UTF-8 >/dev/null ||  
                        echo "ERROR: Invalid UTF-8 found: $B4b1$B4b2$B4b3$B4b4"; exit 40; 
                  #
              done
          done
      done
  done
  echo "# Test 4 bytes for Valid UTF-8 values: END"
  echo
fi

########################################################################
# There is no test (yet) for negated range values in the astral plane. #  
#                           (all negated range values must be invalid) #
#  I won't bother; This was mainly for me to ge the general feel of    #     
#   the tests, and the final test below should flush anything out..    #
# Traversing the intire UTF-8 range takes quite a while...             #
#   so no need to do it twice (albeit in a slightly different manner)  #
########################################################################

################################
### The construction of:    ####
###  The Regular Expression ####
###      (de-construction?) ####
################################

#     BYTE 1                BYTE 2       BYTE 3      BYTE 4 
# 1: [x00-x7F]
#    ===========
#    ([x00-x7F])
#
# 2: [xC2-xDF]           [x80-xBF]
#    =================================
#    ([xC2-xDF][x80-xBF])
# 
# 3: [xE0]                [xA0-xBF]  [x80-xBF]   
#    [xED]                [x80-x9F]  [x80-xBF]
#    [xE1-xECxEE-xEF]  [x80-xBF]  [x80-xBF]
#    ==============================================
#    ((([xE0][xA0-xBF])|([xED][x80-x9F])|([xE1-xECxEE-xEF][x80-xBF]))([x80-xBF]))
#
# 4  [xF0]                [x90-xBF]  [x80-xBF]  [x80-xBF]    
#    [xF1-xF3]           [x80-xBF]  [x80-xBF]  [x80-xBF]
#    [xF4]                [x80-x8F]  [x80-xBF]  [x80-xBF]
#    ===========================================================
#    ((([xF0][x90-xBF])|([xF1-xF3][x80-xBF])|([xF4][x80-x8F]))([x80-xBF]2))
#
# The final regex
# ===============
# 1-4:  (([x00-x7F])|([xC2-xDF][x80-xBF])|((([xE0][xA0-xBF])|([xED][x80-x9F])|([xE1-xECxEE-xEF][x80-xBF]))([x80-xBF]))|((([xF0][x90-xBF])|([xF1-xF3][x80-xBF])|([xF4][x80-x8F]))([x80-xBF]2)))
# 4-1:  (((([xF0][x90-xBF])|([xF1-xF3][x80-xBF])|([xF4][x80-x8F]))([x80-xBF]2))|((([xE0][xA0-xBF])|([xED][x80-x9F])|([xE1-xECxEE-xEF][x80-xBF]))([x80-xBF]))|([xC2-xDF][x80-xBF])|([x00-x7F]))


#######################################################################
#  The final Test; for a single character (multi chars to follow)     #  
#   Compare the return code of 'iconv' against the 'regex'            #
#   for the full range of 0x000000 to 0x10FFFF                        #
#                                                                     #     
#  Note; this script has 3 modes:                                     #
#        Run this test TWICE, set each mode Manually!                 #     
#                                                                     #     
#     1. Sequentially test every value from 0x000000 to 0x10FFFF      #     
#     2. Throw a spanner into the works! Force random byte patterns   #     
#     2. Throw a spanner into the works! Force random longer strings  #     
#        ==============================                               #     
#                                                                     #     
#  Note: The purpose of this routine is to determine if there is any  #
#        difference how 'iconv' and 'regex' handle the same data      #  
#                                                                     #     
#######################################################################
if ((Rx==1)) ; then
  # real    191m34.826s
  # user    158m24.114s
  # sys      83m10.676s
  time ((([xE0][xA0-xBF]) # End time
fi
exit

Te mostramos reseñas y calificaciones

Nos puedes auxiliar nuestro estudio ejecutando un comentario o dejando una valoración te damos las gracias.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *