Игнорировать регистр в строках Python

Как проще всего сравнивать строки в Python без учета регистра?

Конечно, можно сделать (str1.lower () ‹= str2.lower ()) и т. Д., Но это создало две дополнительные временные строки (с очевидными накладными расходами alloc / g-c).

Думаю, я ищу эквивалент функции stricmp () в C.

[Требуется еще немного контекста, поэтому я продемонстрирую тривиальный пример:]

Предположим, вы хотите отсортировать длинный список строк. Вы просто выполняете theList.sort (). Это сравнение строк O (n * log (n)) без управления памятью (поскольку все строки и элементы списка являются своего рода интеллектуальными указателями). Вы счастливы.

Теперь вы хотите сделать то же самое, но не обращайте внимания на регистр (давайте упростим и скажем, что все строки - это ascii, поэтому проблемы с языковым стандартом можно игнорировать). Вы можете выполнить theList.sort (key = lambda s: s.lower ()), но тогда вы вызываете два новых выделения для каждого сравнения, а также загружаете сборщик мусора дублированными (пониженными) строками. Каждый такой шум управления памятью на порядки медленнее, чем простое сравнение строк.

Теперь с помощью функции, аналогичной stricmp (), вы выполняете: theList.sort (cmp = stricmp), и это так же быстро и удобно для памяти, как theList.sort (). Вы снова счастливы.

Проблема в том, что любое сравнение на основе Python без учета регистра включает неявное дублирование строк, поэтому я ожидал найти сравнения на основе C (возможно, в строке модуля).

Ничего подобного найти не удалось, отсюда и вопрос. (Надеюсь, это проясняет вопрос).


person Paul Oyster    schedule 15.09.2008    source источник
comment
Эквивалент PHP: strcasecmp - nl3.php.net/strcasecmp   -  person fijter    schedule 15.09.2008
comment
ваши предположения ошибочны. list.sort () с ключом = not означает два новых выделения для каждого сравнения. (list.sort с cmp =, с другой стороны, действительно вызывает аргумент для каждого сравнения)   -  person    schedule 23.09.2008
comment
попытался переименовать вопрос с Ignore case in python strings на What's closest to stricmp in Python for 7-bit ascii string comparison?, чтобы более точно отразить фактический вопрос оператора. основная проблема: unicode также является "строкой", но этот вопрос может полностью ошибиться; см. комментарии tchrist   -  person n611x007    schedule 15.04.2014
comment
связанные: Как мне свернуть строку в Python 2?   -  person jfs    schedule 29.09.2015


Ответы (15)


В ответ на ваше разъяснение ...

Вы можете использовать ctypes для выполнения функции c "strcasecmp". Ctypes включен в Python 2.5. Он предоставляет возможность обращаться к dll и разделяемым библиотекам, таким как libc. Вот краткий пример (Python в Linux; см. Ссылку для справки по Win32):

from ctypes import *
libc = CDLL("libc.so.6")  // see link above for Win32 help
libc.strcasecmp("THIS", "this") // returns 0
libc.strcasecmp("THIS", "THAT") // returns 8

также может потребоваться ссылка на документацию по strcasecmp

Не совсем уверен, что это быстрее или медленнее (не тестировал), но это способ использовать функцию C для сравнения строк без учета регистра.

~~~~~~~~~~~~~~

Код ActiveState - рецепт 194371: строки без учета регистра - это рецепт создание строкового класса без учета регистра. Это может быть немного излишне kill для чего-то быстрого, но может предоставить вам общий способ обработки нечувствительных к регистру строк, если вы планируете часто их использовать.

person patrickyoung    schedule 15.09.2008
comment
Я хорошо знаю этот рецепт, но за кулисами у него просто есть дубликат в нижнем регистре для каждой строки, что нехорошо (как объяснено в тривиальном примере, который я добавил) - person Paul Oyster; 16.09.2008
comment
Решение ctype - это то, что я искал, спасибо. Для справки, вот код win32: from ctypes import * clib = cdll.LoadLibrary (msvcrt) theList = [abc, ABC, def, DEF] * 1000000 theList.sort (cmp = clib._stricmp) - person Paul Oyster; 16.09.2008
comment
это намного медленнее. смотри мой ответ! - person ; 23.09.2008
comment
Я считаю, что это дает неправильный ответ для строк с нулями. - person Darius Bacon; 29.10.2008
comment
Нет, это неправильно. Единственное правильное решение - сравнить их регистры Unicode. Иначе облажаетесь. - person tchrist; 11.08.2011
comment
@tchrist в качестве примечания, строка 'unicode' не является предметом вопроса. Тем не менее, ваш комментарий, который, кажется, указывает любому, кто приходит сюда с надеждой на Unicode, в правильном направлении, неоценим. Я пытаюсь отредактировать заголовок вопроса, чтобы более точно отразить его. - person n611x007; 15.04.2014

Вот тест, показывающий, что при использовании str.lower быстрее, чем предложенный метод принятого ответа (libc.strcasecmp):

#!/usr/bin/env python2.7
import random
import timeit

from ctypes import *
libc = CDLL('libc.dylib') # change to 'libc.so.6' on linux

with open('/usr/share/dict/words', 'r') as wordlist:
    words = wordlist.read().splitlines()
random.shuffle(words)
print '%i words in list' % len(words)

setup = 'from __main__ import words, libc; gc.enable()'
stmts = [
    ('simple sort', 'sorted(words)'),
    ('sort with key=str.lower', 'sorted(words, key=str.lower)'),
    ('sort with cmp=libc.strcasecmp', 'sorted(words, cmp=libc.strcasecmp)'),
]

for (comment, stmt) in stmts:
    t = timeit.Timer(stmt=stmt, setup=setup)
    print '%s: %.2f msec/pass' % (comment, (1000*t.timeit(10)/10))

типичное время на моей машине:

235886 words in list
simple sort: 483.59 msec/pass
sort with key=str.lower: 1064.70 msec/pass
sort with cmp=libc.strcasecmp: 5487.86 msec/pass

Итак, версия с str.lower не только самая быстрая, но также самая портативная и питоническая из всех предлагаемых здесь решений. Я не анализировал использование памяти, но в оригинальном плакате все еще не было веских причин для беспокойства. Кроме того, кто сказал, что вызов модуля libc не дублирует никаких строк?

NB: строковый метод lower() также имеет то преимущество, что он зависит от локали. Что-то, что вы, вероятно, не поймете при написании собственного «оптимизированного» решения. Тем не менее, из-за ошибок и отсутствующих функций в Python такое сравнение может дать неверные результаты в контексте Unicode.

person Community    schedule 23.09.2008
comment
Конечно, проблема с памятью, поскольку более 99,9% времени .lower () приходится на выделение памяти. Кроме того, на машинах (Windows), которые я проверял, подход key = _stricmp был в 4-5 раз быстрее и без памяти. - person Paul Oyster; 07.10.2008
comment
В 4-5 раз быстрее, чем метод .lower, означает, что он в 2 раза быстрее, чем простой случай сортировки. как это может быть?!? - person ; 10.10.2008
comment
@hop все слова в списке слов, который вы проверяете, уже в нижнем регистре. Это может дать вам результаты, далекие от результатов Павла. - person Virgil Dupras; 16.01.2010
comment
@hop еще раз: забудь. Я пробовал отсортировать тот же список с помощью str.upper, и результаты примерно такие же. - person Virgil Dupras; 16.01.2010
comment
@virgil: также, в моем регионе более двух третей всех слов начинаются с заглавной буквы;) - person ; 16.01.2010
comment
@hop представленный вами анализ действительно хорош. это показывает (особенно мне), что иногда лучше не считать циклы и байты в уме. - person Xolve; 13.11.2010
comment
Вопрос для пояснения: что вы имеете в виду под str.lower зависит от локали? Я спрашиваю, потому что в вашем ответе нет другого упоминания о вещах, связанных с локалью, например, звонит locale.setlocale. - person tzot; 23.03.2011
comment
@ ΤΖΩΤΖΙΟΥ: из документации: для 8-битных строк этот метод зависит от локали. Мне не нужно явно устанавливать locale, поскольку это обрабатывается моей системной конфигурацией. - person ; 23.03.2011
comment
@hop: мой собственный тест показывает, что метод libc в 3 раза быстрее, чем .lower (). На самом деле вы демонстрируете, что сортировка с помощью ключа более эффективна, чем сортировка с помощью cmp. - person bukzor; 18.05.2011
comment
@hop: тогда ваше утверждение str.lower является самым быстрым на сегодняшний день, что вводит в заблуждение. - person bukzor; 18.05.2011
comment
Это тоже неправильно, потому что, если вы не используете регистр Unicode, вы получите самые разные ответы. - person tchrist; 11.08.2011
comment
@tchrist: ну да, но 1) это применимо ко всем решениям и 2) с помощью strcasecmp вы даже не можете решить эту проблему. - person ; 12.08.2011
comment
@hop: установите флажок bugs.python.org для ошибок Unicode. Я только что представил кучу тестовых примеров, показывающих, где Python дает сбой, не используя casefolding. Если мне нужно выбирать между быстрым и правильным, я знаю, какой из них выберу каждый раз. - person tchrist; 12.08.2011
comment
@tchrist: я не совсем понимаю, к чему вы идете. какое конкретное предлагаемое решение? - person ; 12.08.2011
comment
@hop: должен быть строковый метод, обеспечивающий регистр Unicode, тогда вы просто выполняете простое сравнение. Не связывайтесь с уродливыми антипортативными локали, черт возьми, они так же плохи, как страницы кода GAG в µsløth. string.lower(), string.title(), string.upper() у вас уже есть, но вам нужно string.fold(), чтобы вы могли сделать str1.fold() == str2.fold(). Его нет в Python API. Обратите внимание, как это работает в отделении интенсивной терапии. Потому что это то, что вам нужно; это как выполнять сравнение текста Unicode без учета регистра, не углубляясь в алгоритм сортировки Unicode. - person tchrist; 12.08.2011
comment
@tchrist: есть ли еще реальное решение, которое вы можете предложить? некоторым из нас нужно отправить код сейчас. я очень ценю ваши знания и участие в этой теме, но использование key= с вашим fold() по-прежнему лучше, чем использование cmp= с вашим fold(), и PyICU все равно будет лучше, чем взломать что-то вместе с ctypes, и что? является? ваш? точка? - person ; 12.08.2011
comment
@tchrist: Хорошо, раз уж тебе нужно было перейти на личную тему, это последнее, что я скажу по этому поводу. Вопрос касался проблем, связанных с распределением памяти, этот ответ - о том, чтобы показать, что оптимизация для дублирования строк не улучшит, а снизит производительность, и у ВАС даже нет ни малейшего доказательства того, что во всем этом участвует человеческий язык. OP может сравнивать строки base64 для всего, что вы знаете. Так что перестань быть таким ханжеским, пожалуйста. - person ; 12.08.2011
comment
Вероятно, лучше не называть чей-то ответ глупым. - person Chris Dutrow; 09.11.2011
comment
@DutrowLLC: да, наверное, ты не прав. - person ; 10.11.2011
comment
Этот тест, похоже, полностью пропускает время, которое версия .lower() тратит на сборщик мусора .. верно? - person Izkata; 21.12.2012
comment
Включение gc еще больше ухудшает относительную производительность метода libc. Я обновил свой ответ, чтобы запустить timeit () с включенным gc. - person ; 15.01.2015

Ваш вопрос подразумевает, что вам не нужен Unicode. Попробуйте следующий фрагмент кода; если это сработает для вас, все готово:

Python 2.5.2 (r252:60911, Aug 22 2008, 02:34:17)
[GCC 4.3.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.setlocale(locale.LC_COLLATE, "en_US")
'en_US'
>>> sorted("ABCabc", key=locale.strxfrm)
['a', 'A', 'b', 'B', 'c', 'C']
>>> sorted("ABCabc", cmp=locale.strcoll)
['a', 'A', 'b', 'B', 'c', 'C']

Уточнение: если это не очевидно на первый взгляд, locale.strcoll кажется вам нужной функцией, избегая "повторяющихся" строк str.lower или locale.strxfrm.

person tzot    schedule 15.09.2008
comment
Глобальная настройка locale.setlocale () явно излишняя (слишком глобальная). - person Paul Oyster; 16.09.2008
comment
Я не знаю, что является очевидным излишеством, и глобальная настройка может быть настолько локализована, насколько вам нравится (кроме случаев, когда вы работаете с потоками и вам нужно, чтобы некоторые потоки были локализованы, а некоторые нет по какой-то причине). - person tzot; 30.10.2008
comment
Это единственное решение, которое дает результаты, которые могут правильно взаимодействовать с нечувствительными к регистру утилитами, такими как сортировка Unix с параметром -f. Например, str.lower заставляет A_ сортировать перед AA. - person Neil Mayhew; 09.01.2011
comment
Вы не можете использовать локали POSIX и strcoll, потому что они ненадежны для разных платформ. Вы должны использовать регистр Unicode, который гарантированно будет работать одинаково везде. - person tchrist; 11.08.2011

Используете ли вы это сравнение в очень часто выполняемом пути высокопроизводительного приложения? Или вы запускаете это на строках размером в мегабайты? Если нет, то вам не стоит беспокоиться о производительности и просто использовать метод .lower ().

Следующий код демонстрирует, что выполнение сравнения без учета регистра путем вызова .lower () для двух строк, каждая из которых имеет размер почти мегабайт, занимает около 0,009 секунды на моем настольном компьютере с частотой 1,8 ГГц:

from timeit import Timer

s1 = "1234567890" * 100000 + "a"
s2 = "1234567890" * 100000 + "B"

code = "s1.lower() < s2.lower()"
time = Timer(code, "from __main__ import s1, s2").timeit(1000)
print time / 1000   # 0.00920499992371 on my machine

Если это действительно чрезвычайно важный и критичный к производительности раздел кода, я рекомендую написать функцию на C и вызывать ее из кода Python, поскольку это позволит вам выполнять действительно эффективный поиск без учета регистра. Подробную информацию о написании модулей расширения C можно найти здесь: https://docs.python.org/exnding/exnding.html

person Eli Courtwright    schedule 15.09.2008
comment
вот как вы передаете материал в класс Timer. спасибо за решение совсем другого моего зуда :) - person Manav; 24.03.2011
comment
Это совершенно неверно. Невозможно незаметно обнаружить, что ΣΤΙΓΜΑΣ и στιγμας являются одним и тем же регистром. Вы не должны использовать casemapping для сравнения регистра в Unicode. Вы должны использовать casefolding. Это разные вещи. Σ, σ, ς одинаковы, как и S, ſ, s (а что вообще с s? :) и Μ, μ, µ есть. Есть бесчисленное множество других подобных обстоятельств, например, как weiß, WEIẞ, weiss, WEISS тоже одинаковы или эффективны, эффективны. Вы должны использовать casefold, потому что casemaps не работают. - person tchrist; 11.08.2011

Я не могу найти другой встроенный способ сравнения без учета регистра: рецепт поваренной книги python использует lower ().

Однако вы должны быть осторожны при использовании нижнего значения для сравнения из-за проблемы турецкого языка I. К сожалению, Python не очень хорошо справляется с турецкими языками. ı преобразуется в I, но не преобразуется в ı. İ преобразуется в i, но i не преобразуется в İ.

person Douglas Leeder    schedule 15.09.2008
comment
Как вы видели, Python не очень надежно обрабатывает Unicode. В картах дела на это не обращают внимания. Очень грустный. - person tchrist; 11.08.2011

Нет встроенного эквивалента той функции, которую вы хотите.

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

Если вы не работаете с очень длинными строками (настолько длинными, что при дублировании могут возникнуть проблемы с памятью), я бы сделал это простым и использовал

str1.lower() == str2.lower()

Ты будешь в порядке

person Ricardo Reyes    schedule 15.09.2008
comment
Никогда не говори никогда :) Нет никакого встроенного эквивалента, абсолютного; Знаю, что ни один встроенный аналог был бы ближе к истине. locale.strcoll с учетом регистра LC_COLLATE (как en_US) является встроенным. - person tzot; 16.09.2008
comment
Это неверный ответ. Единственный правильный способ - str1.fold() == str2.fold(), но для этого требуется расширение класса строки Python по умолчанию, который поддерживает полный регистр Unicode строки. Это недостающая функция. - person tchrist; 12.08.2011
comment
@tchrist unclearr: есть ли такое расширение? - person n611x007; 15.04.2014

В этом вопросе задаются 2 очень разные вещи:

  1. Как проще всего сравнивать строки в Python без учета регистра?
  2. Думаю, я ищу эквивалент функции stricmp () в C.

Поскольку на № 1 уже дан очень хороший ответ (например: str1.lower () ‹str2.lower ()), я отвечу на № 2.

def strincmp(str1, str2, numchars=None):
    result = 0
    len1 = len(str1)
    len2 = len(str2)
    if numchars is not None:
        minlen = min(len1,len2,numchars)
    else:
        minlen = min(len1,len2)
    #end if
    orda = ord('a')
    ordz = ord('z')

    i = 0
    while i < minlen and 0 == result:
        ord1 = ord(str1[i])
        ord2 = ord(str2[i])
        if ord1 >= orda and ord1 <= ordz:
            ord1 = ord1-32
        #end if
        if ord2 >= orda and ord2 <= ordz:
            ord2 = ord2-32
        #end if
        result = cmp(ord1, ord2)
        i += 1
    #end while

    if 0 == result and minlen != numchars:
        if len1 < len2:
            result = -1
        elif len2 < len1:
            result = 1
        #end if
    #end if

    return result
#end def

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

Я работаю только со строками ascii, я не уверен, как это будет вести себя с юникодом.

person trevorcroft    schedule 30.08.2011

Когда что-то плохо поддерживается в стандартной библиотеке, я всегда ищу пакет PyPI. С виртуализацией и повсеместным распространением современных дистрибутивов Linux я больше не избегаю расширений Python. PyICU, похоже, отвечает всем требованиям: https://stackoverflow.com/a/1098160/3461

Теперь есть вариант - чистый питон. Это хорошо протестировано: https://github.com/jtauber/pyuca


Старый ответ:

Мне нравится решение с регулярными выражениями. Вот функцию, которую вы можете скопировать и вставить в любую функцию благодаря поддержке блочной структуры Python.

def equals_ignore_case(str1, str2):
    import re
    return re.match(re.escape(str1) + r'\Z', str2, re.I) is not None

Поскольку вместо поиска я использовал совпадение, мне не нужно было добавлять курсор (^) к регулярному выражению.

Примечание. Это проверяет только равенство, что иногда бывает необходимо. Я бы также не стал говорить, что мне это нравится.

person Community    schedule 26.04.2010
comment
[Я бы хотел, чтобы для этого был виртуальный штамп] Не используйте $, используйте \Z. Прочтите фантастическое руководство, чтобы узнать, что на самом деле делает $; не полагайтесь на легенды, догадки или что-то еще. - person John Machin; 26.04.2010
comment
Я изменил это. Я также включил функцию вики сообщества для своего ответа. Спасибо. - person Benjamin Atkin; 26.04.2010
comment
Подходит только для проверки на равенство, что не совсем то же самое, что сравнение двух строк и определение того, является ли одна из них меньше, равна или больше другой. - person martineau; 29.08.2014
comment
@martineau, спасибо. Я добавил примечание, а также немного поискал и нашел решение, которое, как мне кажется, мне было бы более комфортно, и обновил свой ответ с его помощью. Однако это не полный ответ. Надеюсь, кто-нибудь (я, если я дойду до этого) узнает, как работает одна из этих библиотек, и предоставит образец кода. - person Benjamin Atkin; 03.09.2014
comment
Да, похоже, что расширение pyuca (Python Unicode Collation Algorithm) может работать, потому что отчет, на котором оно основано - Алгоритм сортировки Unicode (UCA) - говорит, что различия в регистре (прописные и строчные) обычно игнорируются. - person martineau; 04.09.2014

Вот как вы это сделаете с re:

import re
p = re.compile('^hello$', re.I)
p.match('Hello')
p.match('hello')
p.match('HELLO')
person Moses Ting    schedule 15.09.2008
comment
Регулярные выражения без учета регистра могут использоваться только для проверки равенства (Истина / Ложь), но не для сравнения (меньше / равно / больше) - person tzot; 16.09.2008

Рекомендуемая идиома для сортировки списков значений с использованием дорогостоящих в вычислении ключей - это так называемый «декорированный шаблон». Он состоит просто в построении списка кортежей (ключей, значений) из исходного списка и сортировке этого списка. Затем просто удалить ключи и получить список отсортированных значений:

>>> original_list = ['a', 'b', 'A', 'B']
>>> decorated = [(s.lower(), s) for s in original_list]
>>> decorated.sort()
>>> sorted_list = [s[1] for s in decorated]
>>> sorted_list
['A', 'a', 'B', 'b']

Или, если вам нравятся однострочные:

>>> sorted_list = [s[1] for s in sorted((s.lower(), s) for s in original_list)]
>>> sorted_list
['A', 'a', 'B', 'b']

Если вы действительно беспокоитесь о стоимости вызова lower (), вы можете просто хранить кортежи (пониженная строка, исходная строка) везде. Кортежи - это самый дешевый вид контейнеров в Python, они также являются хешируемыми, поэтому их можно использовать в качестве ключей словаря, членов набора и т. Д.

person Antoine P.    schedule 15.09.2008
comment
тупли дешевы, а вот дублирование струн - нет ... - person Paul Oyster; 16.09.2008
comment
это также то, что делает сортировка python с аргументом key =. - person ; 23.09.2008
comment
Это 7-битный образ мышления, который совершенно не подходит для данных Unicode. Вы должны использовать либо полный регистр Unicode, либо основную силу сортировки в соответствии с алгоритмом сортировки Unicode. Да, в любом случае это означает новые копии строки, но, по крайней мере, тогда вы можете провести двоичное сравнение вместо того, чтобы рыться в таблицах для каждой кодовой точки. - person tchrist; 11.08.2011

Я почти уверен, что вам нужно либо использовать .lower (), либо регулярное выражение. Мне неизвестна встроенная функция сравнения строк без учета регистра.

person Mark Biek    schedule 15.09.2008

Для случайных или даже повторяющихся сравнений несколько дополнительных строковых объектов не должны иметь значения, если это не произойдет во внутреннем цикле вашего основного кода или у вас недостаточно данных, чтобы действительно заметить влияние на производительность. Посмотрите, если вы это сделаете: делать что-то «глупым» способом будет гораздо менее глупо, если вы также будете делать это меньше.

Если вы серьезно хотите продолжать сравнивать много-много текста без учета регистра, вы можете каким-то образом держать под рукой строчные версии строк, чтобы избежать завершения и повторного создания, или нормализовать весь набор данных в нижнем регистре. Конечно, это зависит от размера набора данных. Если имеется относительно немного иголок и большой стог сена, замена иголок скомпилированными объектами регулярного выражения является одним из решений. Если Трудно сказать, не увидев конкретного примера.

person Community    schedule 15.09.2008

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

Обратите внимание, что этот метод можно использовать не только для решения проблем с верхним / нижним регистром, но и для других типов сортировки, таких как сортировка по локали или сортировка заголовков «в библиотечном стиле», которая игнорирует ведущие статьи и иным образом нормализует данные перед их сортировкой.

person Dale Wilson    schedule 15.09.2008
comment
вопрос более общий, чем сам пример (на самом деле, в реальных сценариях жизни вы не хотите, чтобы вас беспокоили, прикрепляя строчную версию к каждой строке, которая может понадобиться icmp () позже), но даже в этом тривиальном примере вы не не хочу удваивать память только для того, чтобы иметь возможность сортировать ... - person Paul Oyster; 16.09.2008

Просто используйте метод str().lower(), если не важна высокая производительность - в этом случае напишите этот метод сортировки как расширение C.

"Как написать расширение Python" кажется неплохим вступлением. .

Что еще более интересно, В этом руководстве сравнивается использование библиотеки ctypes и написание внешнего модуля C. (ctype значительно медленнее, чем расширение C).

person dbr    schedule 11.10.2008

Вы можете создать подкласс str и создать свой собственный строковый класс без учета регистра, но ИМХО, это было бы крайне неразумно и создаст гораздо больше проблем, чем оно того стоит.

person Dave Webb    schedule 15.09.2008