web-dev-qa-db-fra.com

grep regex. * ne correspond pas à tout

J'ai récemment commencé à utiliser des outils tels que grep, wc, cat, etc., car je dois gérer de très gros fichiers CSV (> 10 Go) qui ne sont pas assez délimités. correctement (par exemple, avoir des occurrences du caractère délimiteur à l'intérieur de certains champs.

Dans mon travail avec l'un de ces fichiers, j'ai exécuté la commande suivante afin d'essayer de trouver un moyen d'identifier correctement les instances de _;_ qui est un délimiteur et de les remplacer par un autre caractère:

_grep -v -n --text "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]" < Transactions.csv
_

La regex peut probablement être faite beaucoup mieux, mais quand même; Ce qui est surprenant, c’est que, entre autres, le code ci-dessus affiche la ligne suivante:

_12345678:2016-10-25;12345678912345;2016-10-25;gobbledegook �IDNR: 69 ;12345.67;.00;2003-09-05;12345678;2003-09-03;stuff stuff         ;12345 fgadfkjgbsdkb;12/3/45678/9
_

(S'agissant de données de transaction, j'ai modifié la plupart des valeurs des champs, à l'exception de la valeur incriminée __) Peut-être que je suis idiot, mais pourquoi la regex ci-dessus ne correspond-elle pas à cette ligne? Il semble que la regex _.*_ ne corresponde pas à ce caractère pour une raison quelconque.

Je soupçonne que le fichier est enregistré en utilisant le codage UTF-16, si cela fait une différence.

Edit: Merci à @exore pour la réponse. En fin de compte, mon fichier a été encodé en ISO-8859-15, que j’ai pu comprendre en grep en extrayant les lignes contenant des caractères spéciaux, qui étaient relativement peu nombreux, dans un fichier et en l’ouvrant dans gedit. J'ai ensuite utilisé iconv pour convertir cela en utf8, après quoi tout a bien fonctionné!

1
Liam Baker

C'est un problème typique de codage de caractères. . signifie n'importe quel caractère. Mais quelle séquence d'octet est un caractère légal est une question de codage. Traiter un texte sans connaître le codage est un échec certain. Votre commande grep attend probablement une chaîne encodée en UTF-8. UTF-8 est un encodage multi-octets, ce qui signifie que certains caractères sont représentés par plusieurs octets. Cependant, toutes les séquences d'octets ne sont pas valides. Voir, par exemple, le article de Wikipedia sur UTF-8 .

Lorsque grep rencontre une séquence d'octets qui n'est pas un caractère valide dans l'encodage attendu, il ne peut pas le reconnaître en tant que caractère, la ligne ne correspond pas, elle est sortie. Puisque votre terminal ne reconnaît pas le caractère non plus, vous obtenez un .

Il existe une solution de contournement dans votre cas. Dites à grep de ne pas vous soucier de l'encodage et considérez un octet comme un caractère.

env LANG=C grep ....

ou peut-être

env LANG=C LC_ALL=C grep ....

Vous pouvez tester facilement:

Créez 2 fichiers, un codé en utf-8, un utf-16-be:

$ echo éléphant | tee file.std | iconv -f utf8 -t utf16be >file.utf16be

Vérifier le contenu des fichiers:

$ cat file*
éléphant
�l�phant

Essayez de grep. La chaîne utf16be n'est pas reconnue, pas de sortie:

$ grep '^.*$' file*
file.std:éléphant

N'utilisez pas du tout l'encodage. Un octet est un caractère. � signifie que le terminal ne reconnaît pas la séquence utf16be comme un caractère utf-8 valide. Notez l'utilisation de -a pour indiquer à grep de considérer que le binaire est du texte.

$ env LANG=C grep -a '^.*$' file*
file.std:éléphant
file.utf16be:�l�phant

Si vous connaissez l'encodage, vous pouvez également utiliser iconv pour convertir d'abord votre fichier, puis utiliser grep. Un des suivants devrait fonctionner.

iconv -f utf16   -t utf8 < file | grep ...
iconv -f utf16le -t utf8 < file | grep ...
iconv -f utf16be -t utf8 < file | grep ...
1
exore