【问题标题】:Extract roman numerals from string in python [closed]从python中的字符串中提取罗马数字[关闭]
【发布时间】:2021-06-21 13:41:36
【问题描述】:

我有一个字符串列表,其中包含用拉丁数字表示的和弦,如下所示:

['ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7', 'ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7']

我想将这些字符串分成 3 个子列表,如下所示:

numerals = ['ii', 'vi', 'V', 'IV', 'I', 'V', 'IV', 'ii', 'vi', 'V', 'IV', 'I', 'V', 'IV']
chord_type=['min', 'min', 'maj', 'maj', 'maj', 'maj','maj', 'min', 'min', 'maj', 'maj', 'maj', 'maj','maj']
extentions=['7','7','', 'add9','add9','','7','7','7','','add9','add9','','7']

(如您所见,大写罗马数字对应和弦类型中的“maj”,非大写字母对应“min”。)

在我的例子中所有可能的罗马数字:

i, ii, iii, iv, v, vi, vii, I, II, III, IV, V, VI, VII

请注意,我们不需要 MCLX

我知道我可以在 Python 中从字符串中的字母中提取或拆分数字,如 here 所述,但是如何提取罗马数字?

我考虑过使用类似 match 正则表达式的东西,但我对如何定义这 7 个罗马数字感到困惑,因为这些字符可能会再次出现在字符串中。

【问题讨论】:

  • 你已经添加了python标签,但是你没有为你的问题添加代码。
  • 您是否尝试过使用匹配罗马数字的正则表达式而不是\d+? (请注意,您不需要匹配所有可能的罗马数字,只需匹配从 1 到 7 的那些)
  • 您应该首先获取所有numerals 的列表,也许只是小写。你好像少了一些。您需要 i .. vii 来获得音阶的七个音符。
  • 你能列举所有可能的 a) 数字、b) chord_types 和 c) 扩展吗?
  • 分享你的代码,到目前为止你做了什么?

标签: python string split roman-numerals


【解决方案1】:

如果罗马数字总是在前,那么你可能会这样做

import re
chords = ['ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7', 'ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7']
numerals = [re.match('[IiVv]+', i).group(0) for i in chords]
print(numerals)

输出

['ii', 'vi', 'V', 'IV', 'I', 'V', 'IV', 'ii', 'vi', 'V', 'IV', 'I', 'V', 'IV']

请注意,我使用了 re.match,因为它确实使用了 尝试在字符串的开头应用模式,并限制您的示例中存在的数字(而不是使用所有已知的,即 IiVvXxLlCcDdMm) .

【讨论】:

  • 罗马数字可以有IV以外的字母,包括CLMX
  • @TimBiegeleisen 这些是和弦,所以不需要添加 C、L、M、X
  • @C-Bk 那么在这种情况下,听起来你可能是对的:-)
  • @Daweo 不会也匹配无效的和弦,如 IIiv
【解决方案2】:

您可以使用.startswith 字符串方法非常简单地做到这一点,如下所示:

nummerals = ['i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII']
lst = ['ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7', 'ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7']


nummerals.sort(key=len, reverse=True)  # see note 1

res = [next(n for n in nummerals if y.startswith(n)) for y in lst]  # see note 2
print(res)  # -> ['ii', 'vi', 'V', 'IV', 'I', 'V', 'IV', 'ii', 'vi', 'V', 'IV', 'I', 'V', 'IV']

注意事项

  1. 原始的nummerals 列表必须按长度排序(降序),以确保匹配最大可能的数字(.startswith for 'ii7' 将匹配 'i''ii',但您想要第二个)。
  2. 以上代码可能会抛出StopIteration 错误。如果您想防止这种情况发生,请向next 提供一个备用值。

【讨论】:

    【解决方案3】:

    我的解决方案使用了一个有点复杂的正则表达式,它有两个优点:

    1. 如果一个数字的扩展看起来像是一个不可能的罗马数字的一部分,例如IV,然后是I,一个天真的方法会考虑数字IVI,而我的方法只会考虑IV I 作为扩展名。
    2. 如果您需要使用更大的数字扩展您的应用程序,这将适用于非常大的数字。

    编辑:显然对于和弦来说,更大的数字可能没用,但谁知道呢?也许你会更新音乐的工作方式?

    我使用的正则表达式来自here。我稍微修改了一下,让它在这里工作。

    import re
    
    l = ['ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7', 'ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7']
    
    numerals = []
    chord_type = []
    extensions = []
    
    roman_regex = '^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})'
    
    for e in l:
        roman_search = re.search(roman_regex , e.upper())
        start = roman_search.start()
        end = roman_search.end()
        roman = e[start:end]
    
        numerals.append(roman)
        chord_type.append('maj' if roman[0].upper() == roman[0] else 'min')
        extensions.append(e[end:])
    
    >>> print(numerals)
    ... print(chord_type)
    ... print(extensions)
    
    ['ii', 'vi', 'V', 'IV', 'I', 'V', 'IV', 'ii', 'vi', 'V', 'IV', 'I', 'V', 'IV']
    ['min', 'min', 'maj', 'maj', 'maj', 'maj', 'maj', 'min', 'min', 'maj', 'maj', 'maj', 'maj', 'maj']
    ['7', '7', '', 'add9', 'add9', '', 'maj7', '7', '7', '', 'add9', 'add9', '', 'maj7']
    

    【讨论】:

      【解决方案4】:

      你可以试试这个:

      import re
      
      matcher = re.compile(r'([IiVv]+)(min|maj|)(.*)')
      
      def parse_string(s):
          gs = matcher.findall(s)[0]
          if gs[1] == '':
              gs = (gs[0], 'maj' if gs[0].isupper() else 'min', gs[2])
          return gs
          
      def parse_array(A):
          return [parse_string(chord) for chord in A]
          
      parsed = parse_array(['ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7', 'ii7', 'vi7', 'V', 'IVadd9', 'Iadd9', 'V', 'IVmaj7'])
      
      numerals, chord_type, extensions = zip(*parsed)
      
      print(list(numerals))
      print(list(chord_type))
      print(list(extensions))
      

      我使用re 进行正则表达式解析,这当然没问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多