Вопрос по locale, regex, pcre, unicode, perl – Должны ли мы рассмотреть использование range [a-z] в качестве ошибки?

22

В моем регионе (et_EE)[a-z] средства:

abcdefghijklmnopqrsšz

Итак, 6 символов ASCII (tuvwxy) и один из эстонского алфавита (ž) не включены. Я вижу много модулей, которые до сих пор используют регулярные выражения, такие как

/\A[0-9A-Z_a-z]+\z/

Для меня это неправильный способ определения диапазона буквенно-цифровых символов ASCII, и я думаю, что его следует заменить на:

/\A\p{PosixAlnum}+\z/

Первый все еще считается идиоматическим способом? Или принято решение? Или ошибка?

Или у последнего есть некоторые предостережения?

Perl не интерпретирует[a-z] в соответствии с вашей локалью. Просто используйте такие вещи, как\pL а также\w а также\p{alpha} по мере необходимости. tchrist
Как вы определяете, что[a-z] включает в себяš но нетtuvwxy? Не следует, чтобы классы символов определялись в терминах порядка кодовых точек, а не для какого-либо конкретного языкового сопоставления. bobince
С помощью[a-z] сама по себе не является ошибкой, но может быть причиной одного (или нескольких). Anthony Grist
возможноdesign flaw это лучший вариант, но да, это регулярное выражение может привести к ошибкам в любом программном обеспечении, которое предназначено для поддержки более чем одной локали. (В сторону: этот вопрос выходит далеко за рамки perl, поэтому я бы предложил удалитьperl тег.) kojiro

Ваш Ответ

3   ответа
5
Possible Locale Bugs

с которой вы сталкиваетесь, не связана с классами символов POSIXper se, но с тем, что классы зависят от локали. Например, регулярное выражение (7) говорит:

Within a bracket expression, the name of a character class enclosed in "[:" and ":]" stands for the list of all characters belonging to that class...These stand for the character classes defined in wctype(3). A locale may provide others.

Акцент мой, но на странице руководства четко сказано, что классы персонажей зависят от локали. Далее, wctype (3) говорит:

The behavior of wctype() depends on the LC_CTYPE category of the current locale.

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

Character Classes as Shortcuts

Классы символов - это ярлыки для определения наборов. Вы, конечно, не ограничены предварительно определенными наборами для вашей локали, и вы можете свободно использовать наборы символов Unicode, определенные perlre (1), или просто создавать наборы явно, если это обеспечивает большую точность.

Вы уже знаете это, поэтому я не пытаюсь быть педантичным. Я просто указываю на то, что если вы не можете или не можете исправить локаль (которая является источником проблемы здесь), то вы должны использовать явный набор, как вы это сделали.

Удобный класс удобен, только если он работает для вашего случая использования. Если это не так, выбросьте его за борт!

Вы должны добавить тот факт, что[a-z] это диапазон символов, но не класс.[:alpha:] использует класс
Perl не использует локализованные версии свойств.
6

мне было интересно узнать, как он вообще идет. Тестируя это на популярных языках программирования с поддержкой собственных регулярных выражений, Perl, PHP, Python, Ruby, Java и Javascript, можно сделать следующие выводы:

[a-z] will match ASCII-7 a-z range in each and every one of those languages, always, and locale settings do not impact it in any way. Characters like ž and š are never matched. \w might or might not match characters ž and š, depending on the programming language and parameters given on creation of regular expression. For this expression the variety is greatest, as in some languages they are never matched, irrelevant of options, in others they are always matched and in some it depends. POSIX [[:alpha:]] and Unicode \p{Alpha} and \p{L}, if they are supported by the regular expression system of the programming language in question and appropriate configuration is used, will match characters like like ž and š.

Обратите внимание, что «Соответствующая конфигурация» не требует изменения локали: изменение локали не оказало влияния на результаты ни в одном из протестированных системы.

Чтобы быть в безопасности, я также протестировал командную строку Perl, grep и awk. Оттуда командная строка Perl ведет себя так же, как и выше. Тем не менее, grep и awk, похоже, отличаются от других тем, что для них локаль имеет значение и для[a-z], Поведение также зависит от версии и реализации.

В этом контексте - grep, awk или аналогичные инструменты командной строки - я согласен, что использованиеa-z диапазон без определения локали может считаться ошибкой, так как вы не можете точно знать, что в итоге получается.

Если мы перейдем к более подробной информации по языку, статус будет следующим:

Java

В яве,\p{Alpha} работает как[a-z] если класс Unicode не указан, и Unicode буквенный символ, если он есть, соответствующийž. \w будет соответствовать символам какž если флаг Unicode присутствует, а если нет, и\p{L} будет соответствовать независимо от флага Юникода. Нет регулярных выражений или поддержки[[alpha]].

PHP

В PHP\w, [[:alpha:]] а также\p{L} будет соответствовать символам какž если Юникод-переключатель присутствует, а не если его нет.\p{Alpha} не поддерживается. Локаль не влияет на регулярные выражения.

Python

\w будет соответствовать указанным символам, если присутствует флаг Unicode, а флаг локали отсутствует. Для строк Unicode флаг Unicode принимается по умолчанию, если используется Python 3, но не с Python 2. Unicode\p{Alpha}, \p{L} или POSIX[[:alpha:]] не поддерживаются в Python.

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

Perl

\w соответствует ранее упомянутым символам в дополнение к соответствию[a-z], Unicode\p{Letter}, \p{Alpha} и POSIX[[:alpha:]] поддерживаются и работают как положено. Флаги Unicode и locale для регулярного выражения не изменили результатов, а также не изменили языковой стандарт илиuse locale;/no locale;.

Поведение не меняется, если мы запускаем тесты с использованием командной строки Perl.

Ruby

[a-z] а также\w обнаружить только персонажи[a-z]Неактуальные варианты. Unicode\p{Letter}, \p{Alpha} и POSIX[[:alpha:]] поддерживаются и работают как положено. Локаль не оказывает влияния.

Javascript

[a-z] а также\w всегда обнаруживает только символы[a-z], Есть поддержка/u Переключатель Unicode в ECMA2015, который в основном поддерживается основными браузерами, но не поддерживает[[:alpha:]], \p{Alpha} или же\p{L} или изменить поведение\w, Переключатель Unicode добавляетобработка символов Юникода как одного символа, которая была проблемой раньше.

Ситуация такая же для javascript на стороне клиента, как и для Node.js.

AWK

Для AWK, в статье есть более подробное описание статусаA.8 Регулярные выражения и локации: длинная грустная история, В нем подробно говорится, что в старом мире инструментов Unix,[a-z] был правильный способ обнаружения строчных букв, и именно так работали инструменты того времени. Тем не менее, POSIX 1992 года представил локали и изменил интерпретацию классов символов так, чтобы порядок символов определялся в соответствии с порядком сопоставления, связывая его с локалью. Это было принято AWK того времени (серия 3.x), что привело к нескольким проблемам. Когда была разработана серия 4.x, POSIX 2008 определил порядок неопределенности, и сопровождающий вернулся к исходному поведению.

В настоящее время используется в основном 4.x версия AWK. Когда это используется,[a-z] соответствует a-z, игнорируя любые изменения локали, и\w а также[[:alpha:]] будет соответствовать специфическим для локали символам. Unicode \ p {Alpha} и \ p {L} не поддерживаются.

grep

Grep (а также sed, ed) используют базовые регулярные выражения GNU, что является старым вариантом. Он не поддерживает классы символов Unicode.

По крайней мере GNU grep 2.16 и 2.25, похоже, следуют положению 1992 года в этой области, что также имеет значение для[a-z], а также для\w а также[[:alpha:]], Это означает, например, что [a-z] соответствует только z в множестве xuzv & # xF6; & # xE4; если используется эстонский язык

Тестовый код, указанный ниже для каждого языка.

Java (1.8.0_131)

import java.util.regex.*;
import java.util.Locale;

public class RegExpTest {
    public static void main(String args[]) {
        verify("v", 118);
        verify("š", 353);
        verify("ž", 382);

        tryWith("v");
        tryWith("š");
        tryWith("ž");
    }
    static void tryWith(String input) {
        matchWith("[a-z]", input);
        matchWith("\\w", input);
        matchWith("\\p{Alpha}", input);
        matchWith("\\p{L}", input);
        matchWith("[[:alpha:]]", input);
    }

    static void matchWith(String pattern, String input) {
        printResult(Pattern.compile(pattern), input);
        printResult(Pattern.compile(pattern, Pattern.UNICODE_CHARACTER_CLASS), ,input);
    }
    static void printResult(Pattern pattern, String input) {
        System.out.printf("%s\t%03d\t%5s\t%-10s\t%-10s\t%-5s%n",
          input, input.codePointAt(0), Locale.getDefault(),
          specialFlag(pattern.flags()),
          pattern, pattern.matcher(input).matches());
    }
    static String specialFlag(int flags) {
      if ((flags & Pattern.UNICODE_CHARACTER_CLASS) == Pattern.UNICODE_CHARACTER_CLASS) {
          return "UNICODE_FLAG";
      }
      return "";
    }
    static void verify(String str, int code) {
        if (str.codePointAt(0) != code) {
            throw new RuntimeException("your editor is not properly configured for this character: " + str);
        }
    }
}

PHP (7.1.5)

<?php
/*
PHP, even with 7, only has binary strings that can be operated with unicode-aware
functions, if needed. So functions operating them need to be told which charset to use.

When there is encoding assumed and not specified, PHP defaults to ISO-8859-1.
*/


// PHP7 and extension=php_intl.dll enabled in PHP.ini is needed for IntlChar class
function codepoint($char) {
  return IntlChar::ord($char);
}

function verify($inputp, $code) {
  if (codepoint($inputp) != $code) {
    throw new Exception(sprintf('Your editor is not configured correctly for %s (result %s, should be %s)',
      $inputp, codepoint($inputp), $code));
  }
}

$rowindex = 0;
$origlocale = getlocale();

verify('v', 118);
verify('š', 353); // https://en.wikipedia.org/wiki/%C5%A0#Computing_code
verify('ž', 382); // https://en.wikipedia.org/wiki/%C5%BD#Computing_code

function tryWith($input) {
  matchWith('[a-z]', $input);
  matchWith('\\w', $input);
  matchWith('[[:alpha:]]', $input); // POSIX, http://www.regular-expression,s.info/posixbrackets.html
  matchWith('\p{L}', $input);
}
function matchWith($pattern, $input) {
  global $origlocale;
  selectLocale($origlocale);
  printResult("/^$pattern\$/", $input);
  printResult("/^$pattern\$/u", $input);
  selectLocale('C'); # default (root) locale
  printResult("/^$pattern\$/", $input);
  printResult("/^$pattern\$/u", $input);
  selectLocale(['et_EE', 'et_EE.UTF-8', 'Estonian_Estonia.1257']);
  printResult("/^$pattern\$/", $input);
  printResult("/^$pattern\$/u", $input);
  selectLocale($origlocale);
}
function selectLocale($locale) {
  if (!is_array($locale)) {
    $locale = [$locale];
  }
  // On Windows, no UTF-8 locale can be set
  // https://stackoverflow.com/a/16120506/365237
  // https://msdn.microsoft.com/en-us/library/x99tb11d.aspx
  // Available Windows locales
  // https://docs.moodle.org/dev/Table_of_locales
  $retval = setlocale(LC_ALL, $locale);
  //printf("setting locale %s, retval was %s\n", join(',', $locale), $retval);
  if ($retval === false || $retval === null) {
    throw new Exception(sprintf('Setting locale %s failed', join(',', $locale)));
  }
}
function getlocale() {
  return setlocale(LC_ALL, 0);
}
function printResult($pattern, $input) {
  global $rowindex;
  printf("%2d: %s\t%03d\t%-20s\t%-25s\t%-10s\t%-5s\n",
        $rowindex, $input, codepoint($input), getlocale(),
        specialFlag($pattern), 
        $pattern, (preg_match($pattern, $input) === 1)?'true':'false');
  $rowindex = $rowindex + 1;
}
function specialFlag($pattern) {
  $arr = explode('/',$pattern);
  $lastelem = array_pop($arr);
  if (strpos($lastelem, 'u') !== false) {
    return 'UNICODE';
  }
  return '';
}

tryWith('v');
tryWith('š');
tryWith('ž');

Python (3.5.3)

# -*- coding: utf-8 -*-

# with python, there are two strings: unicode strings and regular ones.
# when you use unicode strings, regular expressions also take advantage of it,
# so no need to tell that separately. However, if you want to be using specific
# locale, that you need to tell.

# Note that python3 regexps defaults to unicode mode if unicode regexp string is used,
# python2 does not. Also strings are unicode strings in python3 by default.

# summary: [a-z] is always [a-z], \w will match if unicode flag is present and
# locale flag is not present, no unicode \p{Letter} or POSIX :alpha: exists.
# Letters outside ascii-7 never match \w if locale-specific
# regexp is used, as it only supports charsets with one byte per character
# (https://lists.gt.net/python/python/850772).

# Note that in addition to standard https://docs.python.org/3/library/re.html, more
# complete https://pypi.python.org/pypi/regex/ third-party regexp library exists.

import re, locale

def verify(inputp, code):
  if (ord(inputp[0]) != code):
    raise Exception('Your editor is not configured correctly for %s (result %s)' % (inputp, ord(inputp[0])))
  return

rowindex = 0
origlocale = locale.getlocale(locale.LC_ALL)  

verify(u'v', 118)
verify(u'š', 353)
verify(u'ž', 382)

def tryWith(input):
  matchWith(u'[a-z]', input)
  matchWith(u'\\w', input)

def matchWith(pattern, input):
  global origlocale
  locale.setlocale(locale.LC_ALL, origlocale)
  printResult(re.compile(pattern), input)
  printResult(re.compile(pattern, re.UNICODE), input)
  printResult(re.compile(pattern, re.UNICODE | re.LOCALE), input)

  matchWith2(pattern, input, 'C') # default (root) locale
  matchWith2(pattern, input, 'et_EE')
  matchWith2(pattern, input, 'et_EE.UTF-8')
  matchWith2(pattern, input, 'Estonian_Estonia.1257') # Windows locale
  locale.setlocale(locale.LC_ALL, origlocale)

def matchWith2(pattern, input, localeParam):
  try:
    locale.setlocale(locale.LC_ALL, localeParam) # default (root) locale
    printResult(re.compile(pattern), input)
    printResult(re.compile(pattern, re.UNICODE), input)
    printResult(re.compile(pattern, re.UNICODE | re.LOCALE), input)
  except locale.Error:
    print("Locale %s not supported on this platform" % localeParam)

def printResult(pattern, input):
  global rowindex
  try:
    print("%2d: %s\t%03d\t%-20s\t%-25s\t%-10s\t%-5s" % \
          (rowindex, input, ord(input[0]), locale.getlocale(), \
          specialFlag(pattern.flags), \
          pattern.pattern, pattern.match(input) != None))
  except UnicodeEncodeError:
    print("%2d: %s\t%03d\t%-20s\t%-25s\t%-10s\t%-5s" % \
          (rowindex, '?', ord(input[0]), locale.getlocale(), \
          specialFlag(pattern.flags), \
          pattern.pattern, pattern.match(input) != None))
  rowindex = rowindex + 1      

def specialFlag(flags):
  ret = []
  if ((flags & re.UNICODE) == re.UNICODE):
    ret.append("UNICODE_FLAG")
  if ((flags & re.LOCALE) == re.LOCALE):
    ret.append("LOCALE_FLAG")
  return ','.join(ret)

tryWith(u'v')
tryWith(u'š')
tryWith(u'ž')

Perl (V5.22.3)

# Summary: [a-z] is always [a-z], \w always seems to recognize given test chars and
# unicode \p{Letter}, \p{Alpha} and POSIX :alpha: are supported.
# Unicode and locale flags for regular expression didn't matter in this use case.

use warnings;
use strict;
use utf8;
use v5.14;
use POSIX qw(locale_h);
use Encode;
binmode STDOUT, "utf8";

sub codepoint {
  my $inputp = $_[0];
  return unpack('U*', $inputp);
}
sub verify {
  my($inputp, $code) = @_;
  if (codepoint($inputp) != $code) {
    die sprintf('Your editor is not configured correctly for %s (result %s)', $inputp, codepoint($inputp))
  }
}

sub getlocale {
  return setlocale(LC_ALL);
}
my $rowindex = 0;
my $origlocale = getlocale();

verify('v', 118);
verify('š', 353);
verify('ž', 382);

# printf('orig locale is %s', $origlocale);

sub tryWith {
  my ($input) = @_;
  matchWith('[a-z]', $input);
  matchWith('\w', $input);
  matchWith('[[:alpha:]]', $input);
  matchWith('\p{Alpha}', $input);
  matchWith('\p{L}', $input);
}

sub matchWith {
  my ($pattern, $input) = @_;
  my @locales_to_test = ($origlocale, 'C','C.UTF-8', 'et_EE.UTF-8', 'Estonian_Estonia.UTF-8');
  for my $testlocale (@locales_to_test) {
    use locale;
    # printf("Testlocale %s\n", $testlocale);
    setlocale(LC_ALL, $testlocale);
    printResult($pattern, $input, '');
    printResult($pattern, $input, 'u');
    printResult($pattern, $input, 'l');
    printResult($pattern, $input, 'a');
   };
  no locale;
  setlocale(LC_ALL, $origlocale);
  printResult($pattern, $input, '');
  printResult($pattern, $input, 'u');
  printResult($pattern, $input, 'l');
  printResult($pattern, $input, 'a');
}


sub printResult{
  no warnings 'locale';
              # for this test, as we want to be able to test non-unicode-compliant locales as well
              # remove this for real usage

  my ($pattern, $input, $flags) = @_;
  my $regexp = qr/$pattern/;
  $regexp = qr/$pattern/u if ($flags eq 'u');
  $regexp = qr/$pattern/l if ($flags eq 'l');
  printf("%2d: %s\t%03d\t%-20s\t%-25s\t%-10s\t%-5s\n", 
        $rowindex, $input, codepoint($input), getlocale(),
        $flags, $pattern, (($input =~ $regexp) ? 'true':'false'));
  $rowindex = $rowindex + 1;
}

tryWith('v');
tryWith('š');
tryWith('ž');

Ruby (ruby 2.2.6p396 (2016-11-15 ревизия 56800) [x64-mingw32])

# -*- coding: utf-8 -*-

# Summary: [a-z] and \w are always [a-z], unicode \p{Letter}, \p{Alpha} and POSIX
# :alpha: are supported. Locale does not have impact.

# Ruby doesn't seem to be able to interact very well with locale without 'locale'
# rubygem (https://github.com/mutoh/locale), so that is used.

require 'rubygems'
require 'locale'

def verify(inputp, code)
  if (inputp.unpack('U*')[0] != code)
    raise Exception, sprintf('Your editor is not configured correctly for %s (result %s)', inputp, inputp.unpack('U*')[0])
  end
end

$rowindex = 0
$origlocale = Locale.current
$origcharmap = Encoding.locale_charmap

verify('v', 118)
verify('š', 353)
verify('ž', 382)

# printf('orig locale is %s.%s', $origlocale, $origcharmap)
def tryWith(input)
  matchWith('[a-z]', input)
  matchWith('\w', input)
  matchWith('[[:alpha:]]', input)
  matchWith('\p{Alpha}', input)
  matchWith('\p{L}', input)
end  

def matchWith(pattern, input)
  locales_to_test = [$origlocale, 'C', 'et_EE', 'Estonian_Estonia']
  for testlocale in locales_to_test
    Locale.current = testlocale
    printResult(Regexp.new(pattern), input)
    printResult(Regexp.new(pattern.force_encoding('utf-8'),Regexp::FIXEDENCODING), input)
  end
  Locale.current = $origlocale
end

def printResult(pattern, input)
  printf("%2d: %s\t%03d\t%-20s\t%-25s\t%-10s\t%-5s\n", 
        $rowindex, input, input.unpack('U*')[0], Locale.current,
        specialFlag(pattern),
        pattern, !pattern.match(input).nil?)
  $rowindex = $rowindex + 1
end

def specialFlag(pattern)
  return pattern.encoding
end

tryWith('v')
tryWith('š')
tryWith('ž')

Javascript (node.js) (V6.10.3)

function match(pattern, input) {
    try {
        var re = new RegExp(pattern, "u");
        return input.match(re) !== null;
    } catch(e) {
        return 'unsupported';
    }
}
function regexptest() {
    var chars = [
        String.fromCodePoint(118),
        String.fromCodePoint(353),
        String.fromCodePoint(382)
    ];
    for (var i = 0; i < chars.length; i++) {
        var char = chars[i];
        console.log(
            char
            +'\t'
            + char.codePointAt(0)
            +'\t'
            +(match("[a-z]", char))
            +'\t'
            +(match("\\w", char))
            +'\t'
            +(match("[[:alpha:]]", char))
            +'\t'
            +(match("\\p{Alpha}", char))
            +'\t'
            +(match("\\p{L}", char))
            );
    }
}

regexptest();

Javascript (web browsers)

function match(pattern, input) {
    try {
        var re = new RegExp(pattern, "u");
        return input.match(re) !== null;
    } catch(e) {
        return 'unsupported';
    }
}
window.onload = function() {
    var chars = [
        String.fromCodePoint(118),
        String.fromCodePoint(353),
        String.fromCodePoint(382)
    ];
    for (var i = 0; i < chars.length; i++) {
        var char = chars[i];
        var table = document.getElementById('results');
        table.innerHTML += 
            '<tr><td>' + char
            +'</td><td>'
            + char.codePointAt(0)
            +'</td><td>'
            +(match("[a-z]", char))
            +'</td><td>'
            +(match("\\w", char))
            +'</td><td>'
            +(match("[[:alpha:]]", char))
            +'</td><td>'
            +(match("\\p{Alpha}", char))
            +'</td><td>'
            +(match("\\p{L}", char))
            +'</td></tr>';
    }
}
table {
    border-collapse: collapse;
}
table td, table th {
    border: 1px solid black;
}
table tr:first-child th {
    border-top: 0;
}
table tr:last-child td {
    border-bottom: 0;
}
table tr td:first-child,
table tr th:first-child {
    border-left: 0;
}
table tr td:last-child,
table tr th:last-child {
    border-right: 0;
}
<!DOCTYPE html> 
<html>
<head>
    <meta charset="utf-8" /> 
</head>
<body>
    <table id="results">
    <tr>
    	<td>char</td>
    	<td>codepoint</td>
    	<td>[a-z]</td>
    	<td>\w</td>
    	<td>[[:alpha:]]</td>
    	<td>\p{Alpha}</td>
    	<td>\p{L}</td>
    </tr>
    </table>
</body>
</html>

AWK (GNU Awk 4.1.3)

$ echo "xyzvöä" | LC_ALL=C awk '{match($0,"[a-z]+",a)}END{print a[0]}'
xyzv
$ echo "xyzvöä" | LC_ALL=et_EE.utf8 awk '{match($0,"[a-z]+",a)}END{print a[0]}'
xyzv
$ echo "xyzvöä" | LC_ALL=C awk '{match($0,"\\w+",a)}END{print a[0]}'
xyzv
$ echo "xyzvöä" | LC_ALL=et_EE.utf8 awk '{match($0,"\\w+",a)}END{print a[0]}'
xyzvöä
$ echo "xyzvöä" | LC_ALL=C awk '{match($0,"[[:alpha:]]+",a)}END{print a[0]}'
xyzv
$ echo "xyzvöä" | LC_ALL=et_EE.utf8 awk '{match($0,"[[:alpha:]]+",a)}END{print a[0]}'
xyzvöä

AWK (GNU Awk 3.1.8)

$ echo "xyzvöä" | LC_ALL=C awk '{match($0,"[a-z]+",a)}END{print a[0]}'
xyzv
$ echo "xyzvöä" | LC_ALL=et_EE.utf8 awk '{match($0,"[a-z]+",a)}END{print a[0]}'
z
$ echo "xyzvöä" | LC_ALL=C awk '{match($0,"\\w+",a)}END{print a[0]}'
xyzv
$ echo "xyzvöä" | LC_ALL=et_EE.utf8 awk '{match($0,"\\w+",a)}END{print a[0]}'
xyzvöä
$ echo "xyzvöä" | LC_ALL=C awk '{match($0,"[[:alpha:]]+",a)}END{print a[0]}'
xyzv
$ echo "xyzvöä" | LC_ALL=et_EE.utf8 awk '{match($0,"[[:alpha:]]+",a)}END{print a[0]}'
xyzvöä

grep (GNU grep 2.25)

$ echo xuzvöä | LC_ALL=C grep [a-z]
<b>xuzv</b>öä
$ echo xuzvöä | LC_ALL=et_EE.utf8 grep [a-z]
xu<b>z</b>vöä
$ echo xuzvöä | LC_ALL=C grep [[:alpha:]]
<b>xuzv</b>öä
$ echo xuzvöä | LC_ALL=et_EE.utf8 grep [[:alpha:]]
<b>xuzvöä</b>
$ echo xuzvöä | LC_ALL=C grep \\w
<b>xuzv</b>öä
$ echo xuzvöä | LC_ALL=et_EE.utf8 grep \\w
<b>xuzvöä</b>
8

и Perl это отражал.\w означало то же самое, что[0-9A-Z_a-z], И нам понравилось!

Тем не менее, Perl больше не связан с ASCII. Я прекратил использовать[a-z] Некоторое время назад, потому что меня кричали, когда программы, которые я написал, не работали с языками, которые не были английскими. Вы, должно быть, вообразили мое удивление как американца, чтобы обнаружить, что естьat least несколько тысяч человек в этом мире, которые не говорят по-английски.

Perl имеет лучшие способы обработки[0-9A-Z_a-z] тем не мение. Вы можете использовать[[:alnum:]] установить или просто использовать\w который должен делать правильные вещи. Если у вас должны быть только строчные буквы, вы можете использовать[[:lower:]] вместо[a-z] (Который предполагает английский тип языка). (Perl делает несколько шагов, чтобы получить [a-z] означает только 26 символов a, b, c, ... z даже на платформах EBCDIC.)

Если вам нужно указать только ASCII, вы можете добавить/a Классификатор. Если вы имеете в виду конкретную локаль, вы должны скомпилировать регулярное выражение в лексической области «использовать локаль». (Избегайте модификатора / l, так как он применяется только к шаблону регулярного выражения, и ничего больше. Например, в 's / [[: lower:]] / \ U $ & amp; / lg', шаблон компилируется с использованием locale, но \ U - нет. Вероятно, это следует считать ошибкой в Perl, но в настоящее время все работает так. Модификатор / l действительно предназначен только для внутренней бухгалтерии и не должен вводиться напрямую.) На самом деле, лучше перевести ваши языковые данные при вводе в программу и перевести их обратно при выводе, используя внутреннее Юникод. Если ваш языковой стандарт является одним из модных UTF-8, новая функция в 5.16 «использует языковой стандарт»: not_characters & quot; & apos; доступно, чтобы другие части вашего языка работали без проблем в Perl.

$word =~ /^[[:alnum:]]+$/   # $word contains only Posix alphanumeric characters.
$word =~ /^[[:alnum:]]+$/a  # $word contains only ASCII alphanumeric characters.
{ use locale;
  $word =~ /^[[:alnum:]]+$/;# $word contains only alphanum characters for your locale
}

Теперь это ошибка? Если программа не работает должным образом, это простая и понятная ошибка. Если вы действительно хотите последовательность ASCII,[a-z]тогда программист должен был использовать[[:lower:]] с/a Классификатор. Если вы хотите использовать все возможные символы в нижнем регистре, в том числе и на других языках, просто используйте[[:lower:]].

На самом деле это не & # x201C; только POSIX алфавитно-цифровые символы & # x201D ;. Верьте или нет, это эквивалентно\p{XPosixAlnum} не для\p{PosixAlnum}, Почему кто-то использует неуклюжий синтаксис POSIX вместо символьных свойств, мне не понятно. Держись подальше от/l, Декодировать вещи в Unicode; не оставляйте в незашифрованном виде.
У меня есть издания первых трех изданий верблюжьей книги. Мое второе издание даже подписано вами и Ларри. Теперь я вижу четвертое издание книги в феврале, и оно включает в себя целую главу по юникоду. Пора вырвать кредитную карту и объяснить жене, почему я покупаю еще одну версию этой книги.
Вы хотите избежать/l потому что он имеет запах кода: вы забыли декодировать. Вы читали новую версию Unicode для Camel v4 и сопутствующие материалы?
Isn & APOS; т/[\p{PosixAlnum}]/ такой же как/[[:alnum:]]/l а также/[\p{XPosixAlnum}]/ такой же как/[[:alnum:]]/? Зачем избегать/l? Я всегда думал, что это хороший способ убедиться, что передаваемые символы являются частью вашего локального набора символов. Например, русская буква Ro выглядит так же, как латинская букваPТаким образом, PNCBank.com может быть веб-сайтом этого крупного американского банка, базирующегося в Питсбурге, штат Пенсильвания, или он может начинаться с Ro и быть своего рода мошенничеством. В случае OP,/[[:lower]]/l может быть то, что им нужно. Он будет соответствовать эстонским, но не русским символам.
@tchrist Unicode полностью смущает меня, и я в этом отношении далеко не одинок. Большинство разработчиков, с которыми я встречаюсь, признают, что они используют этот беспорядок Unicode. У нас есть некоторые смутные концепции, и мы знаем, что мы делали в прошлом, что-то вроде того, что работает, и что-то, чего нет. Мы просканировали Википедию и наши соответствующие документы по программированию для получения помощи. Есть ли хороший ресурс, который может объяснить Unicode недоумевающим? Что-то, что является руководством, а не просто справочным руководством - хорошийLlama book руководство по Unicode.

Похожие вопросы