Измените локальные настройки, чтобы sed работал правильно, но почему? - PullRequest
2 голосов
/ 14 июля 2011

Ниже приведен файл bash, который я написал для преобразования всех комментариев стиля C ++ (//) в файле C в стиль C (/ ** /).

#!/bin/bash
lang=`echo $LANG`
# It's necessary to change the local setting. I don't know why.
export LANG=C
# Can comment the following statement if there is not dos2unix command.
dos2unix -q $1
sed -i -e 's;^\([[:blank:]]*\)//\(.*\);\1/* \2 */;' $1
export LANG=$lang

Работает.Но я нашел проблему, которую не могу объяснить.По умолчанию мой локальный параметр - en_US.UTF-8.А в моем коде C есть комментарии, написанные на китайском языке, такие как

// some english 一些中文注释

Если я не изменяю локальные настройки, т.е. не запускаю инструкцию export LANG = C Я получу

/* some english */一些中文注释

вместо

/* some english 一些中文注释*/

Я не знаю почему.Я просто нахожу решение методом проб и ошибок.


После прочтения ответа Джонатана Леффлера, я думаю, что я допустил некоторую ошибку, приводящую к некоторому неправильному пониманию.В вопросе эти китайские слова были введены в Google Chrome и не были фактическими словами в моем C-файле.一些 中文 注释 просто означает некоторые китайские комментарии .

Теперь я ввел // немного английского 一 中文 注释 в Visual C ++ 6.0 в Windows XP и скопировал файл cв Debian.Затем я просто запускаю sed -i -e's; ^ ([[: blank:]] ) // (. ); \ 1 / \ 2 /;'$ 1 и получил

/* some english 一些 */中文注释

Я думаю, что разные кодировки символов (GB18030, GBK, UTF-8?) Вызывают разные результаты.

Ниже приведены мои результаты, полученные на Debian

~/sandbox$ uname -a
Linux xyt-dev 2.6.30-1-686 #1 SMP Sat Aug 15 19:11:58 UTC 2009 i686 GNU/Linux
~/sandbox$ echo $LANG
en_US.UTF-8
~/sandbox$ cat tt.c | od -c -t x1
0000000   /   /       s   o   m   e       e   n   g   l   i   s   h    
         2f  2f  20  73  6f  6d  65  20  65  6e  67  6c  69  73  68  20
0000020 322 273 320 251 326 320 316 304 327 242 312 315
         d2  bb  d0  a9  d6  d0  ce  c4  d7  a2  ca  cd
0000034
~/sandbox$ ./convert_comment_style_cpp2c.sh tt.c
~/sandbox$ cat tt.c | od -c -t x1
0000000   /   *           s   o   m   e       e   n   g   l   i   s   h
         2f  2a  20  20  73  6f  6d  65  20  65  6e  67  6c  69  73  68
0000020     322 273 320 251       *   / 326 320 316 304 327 242 312 315
         20  d2  bb  d0  a9  20  2a  2f  d6  d0  ce  c4  d7  a2  ca  cd
0000040
~/sandbox$ 

Я думаю, что эти китайские иероглифы с кодированием 2 байта (Unicode).

Есть еще один пример:

~/sandbox$ cat tt.c | od -c -t x1
0000000   /   /       I   n   W   i   n   d   o   w   :     250 250   ?
         2f  2f  20  49  6e  57  69  6e  64  6f  77  3a  20  a8  a8  3f
0000020   1   ?
         31  3f
0000022
~/sandbox$ ./convert_comment_style_cpp2c.sh tt.c
~/sandbox$ cat tt.c | od -c -t x1
0000000   /   *           I   n   W   i   n   d   o   w   :           *
         2f  2a  20  20  49  6e  57  69  6e  64  6f  77  3a  20  20  2a
0000020   / 250 250   ?   1   ?
         2f  a8  a8  3f  31  3f

Ответы [ 2 ]

5 голосов
/ 14 июля 2011

На какой платформе вы работаете? Ваш скрипт sed отлично работает на MacOS X без изменения локали. Терминал Linux был менее доволен китайскими иероглифами, но он не настроен на использование UTF-8. Более того, шестнадцатеричный дамп полученной строки содержал нулевой байт 0x00, с которого начинали китайцы, что могло привести к путанице. (Замечу, что ваше регулярное выражение добавляет пробел перед текстом комментария, если оно начинается // with a space.)

MacOS X (10.6.8)

Использование команды 'odx' - это программа hex-dump.

$ echo "// some english 一些中文注释" > x3.utf8
$ odx x3.utf8
0x0000: 2F 2F 20 73 6F 6D 65 20 65 6E 67 6C 69 73 68 20   // some english 
0x0010: E4 B8 80 E4 BA 9B E4 B8 AD E6 96 87 E6 B3 A8 E9   ................
0x0020: 87 8A 0A                                          ...
0x0023:
$ utf8-unicode x3.utf8
0x2F = U+002F
0x2F = U+002F
0x20 = U+0020
0x73 = U+0073
0x6F = U+006F
0x6D = U+006D
0x65 = U+0065
0x20 = U+0020
0x65 = U+0065
0x6E = U+006E
0x67 = U+0067
0x6C = U+006C
0x69 = U+0069
0x73 = U+0073
0x68 = U+0068
0x20 = U+0020
0xE4 0xB8 0x80 = U+4E00
0xE4 0xBA 0x9B = U+4E9B
0xE4 0xB8 0xAD = U+4E2D
0xE6 0x96 0x87 = U+6587
0xE6 0xB3 0xA8 = U+6CE8
0xE9 0x87 0x8A = U+91CA
0x0A = U+000A
$ sed 's;^\([[:blank:]]*\)//\(.*\);\1/* \2 */;' x3.utf8
/*  some english 一些中文注释 */
$

Все это выглядит чисто и опрятно.

Linux (RHEL 5)

Я скопировал файл x3.utf8 в коробку Linux и выгрузил его. Затем я запустил на нем скрипт sed, и все казалось в порядке:

$ odx x3.utf8
0x0000: 2F 2F 20 73 6F 6D 65 20 65 6E 67 6C 69 73 68 20   // some english 
0x0010: E4 B8 80 E4 BA 9B E4 B8 AD E6 96 87 E6 B3 A8 E9   ................
0x0020: 87 8A 0A                                          ...
0x0023:
$ sed 's;^\([[:blank:]]*\)//\(.*\);\1/* \2 */;' x3.utf8 | odx
0x0000: 2F 2A 20 20 73 6F 6D 65 20 65 6E 67 6C 69 73 68   /*  some english
0x0010: 20 E4 B8 80 E4 BA 9B E4 B8 AD E6 96 87 E6 B3 A8    ...............
0x0020: E9 87 8A 20 2A 2F 0A                              ... */.
0x0027:
$

Пока все хорошо. Я также попробовал:

$ echo $LANG
en_US.UTF-8
$ echo $LC_CTYPE

$ env | grep LC_
$ bash --version
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
$ cat x3.utf8
// some english 一些中文注释
$ echo $(<x3.utf8)
// some english 一些中文注释
$ sed 's;^\([[:blank:]]*\)//\(.*\);\1/* \2 */;' x3.utf8      
/*  some english 一些中文注释 */
$

Итак, в конце концов, терминал номинально работает в UTF-8, и, похоже, данные отображаются нормально.

Тем не менее, если я повторю строку в терминале, она начинает дрожать. Когда я вырезал и вставил строку в терминал Linux, он сказал:

$ echo "// some english d8d^G:
> "
// some english d8d:

$

и пищит.

$ echo "// some english d8d^G:
> " | odx
0x0000: 2F 2F 20 73 6F 6D 65 20 65 6E 67 6C 69 73 68 20   // some english 
0x0010: 64 38 64 07 3A 0A 0A                              d8d.:..
0x0017:
$

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

$ cat > xxx
's;^\([[:blank:]]*\)//\(.*\);\1/* \2 */;'
// some english d8^@d:^[d8-f^Gf3(i^G

$ odx xxx
0x0000: 27 73 3B 5E 5C 28 5B 5B 3A 62 6C 61 6E 6B 3A 5D   's;^\([[:blank:]
0x0010: 5D 2A 5C 29 2F 2F 5C 28 2E 2A 5C 29 3B 5C 31 2F   ]*\)//\(.*\);\1/
0x0020: 2A 20 5C 32 20 2A 2F 3B 27 0A 2F 2F 20 73 6F 6D   * \2 */;'.// som
0x0030: 65 20 65 6E 67 6C 69 73 68 20 64 38 00 64 3A 1B   e english d8.d:.
0x0040: 64 38 2D 66 07 66 33 28 69 07 0A 0A               d8-f.f3(i...
0x004C:
$

И в этом шестнадцатеричном дампе вы можете увидеть байт 0x00 (смещение 0x003C). Это появляется в позиции, где вы получили конечный комментарий, и нулевой там может запутать sed; но весь ввод такой беспорядок, трудно понять, что с ним делать.

0 голосов
/ 16 июля 2015

Хорошо, вот правильный ответ ...

Библиотека регулярных выражений GNU (regex) не соответствует всем, когда вы вводите . в свое выражение.Да, я знаю, как звучит мозговая смерть.

Проблема возникает из слова «персонаж», теперь разумные люди скажут, что все, что во входном файле для sed - это символы.И даже в вашем случае они совершенно правильны.Но регулярное выражение было запрограммировано так, чтобы требовалось, чтобы ввод был совершенно правильно отформатирован символами текущего набора символов локали (UTF-8), если они правильно отформатированы символы для набора символов Windows (UTF-16), они не являются «символами».

Так как . соответствует только «символам», оно не соответствует вашим символам.

Если вы использовали регулярное выражение //.*$, то есть: закрепили его в конце строкиэто не будет совпадать вообще, потому что между // и концом строки есть что-то, что не является «символом».

И нет, вы не можете сделать ничего подобного //\(.\|[^.]\)*$, это просто невозможносопоставлять эти символы без переключения на локаль C.

Это также иногда нарушает 8-битную прозрачность;То есть: двоичный файл, переданный через sed, будет поврежден, даже если не было внесено никаких изменений.

К счастью, языковой стандарт C все еще использует разумную интерпретацию, поэтому все, что не является правильно отформатированным символом ASCII-68, все еще остается "символом".

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