Python:获取字符串索引的行列号?

4 投票
3 回答
3350 浏览
提问于 2025-04-18 11:37

假设我有一个正在操作的文本文件。内容大概是这样的(希望这不是太难懂):

data_raw = open('my_data_file.dat').read()
matches = re.findall(my_regex, data_raw, re.MULTILINE)
for match in matches:
    try:
        parse(data_raw, from_=match.start(), to=match.end())
    except Exception:
        print("Error parsing data starting on line {}".format(what_do_i_put_here))
        raise

注意在异常处理器里有一个叫 what_do_i_put_here 的变量。我的问题是:我该怎么给这个变量赋值,这样我的脚本就能打印出我想处理的“坏区域”开始的行号?我不介意重新读取文件,只是不知道该怎么做……

3 个回答

0

列的索引是从0开始的,所以在你代码的最后,需要从len(sp[-1])中减去1,才能得到正确的列值。此外,如果字符串的长度为0,或者字符串太短,无法满足索引的要求,我建议返回None(而不是“1.1”,因为这也是错误的,应该是“1.0”)。
总的来说,这是一个很棒且优雅的解决方案,Tim。

def index_to_coordinates(txt:str, index:int) -> str:
    """Returns 'line.column' of index in 'txt'."""
    if not txt or len(txt)-1 < index:
        return None
    sp = txt[:index+1].splitlines(keepends=True)
    return (f"{len(sp)}.{len(sp[-1])-1}")
1

我写了这个代码。虽然还没测试过,而且效率也不高,但它确实能让我的错误信息更清楚一些:

def coords_of_str_index(string, index):
    """Get (line_number, col) of `index` in `string`."""
    lines = string.splitlines(True)
    curr_pos = 0
    for linenum, line in enumerate(lines):
        if curr_pos + len(line) > index:
            return linenum + 1, index-curr_pos
        curr_pos += len(line)

我甚至还没测试过列号是否大致正确。我没有遵循YAGNI原则。

3

这里有一个更简洁的写法,我觉得比你自己的答案更容易理解:

def index_to_coordinates(s, index):
    """Returns (line_number, col) of `index` in `s`."""
    if not len(s):
        return 1, 1
    sp = s[:index+1].splitlines(keepends=True)
    return len(sp), len(sp[-1])

它的工作原理基本和你的答案一样,但通过使用字符串切片的 splitlines() 方法,实际上可以自动计算出你需要的所有信息,而不需要额外的处理。

使用 keepends=True 是必要的,这样才能正确计算行末字符的数量。

唯一需要注意的额外问题是空字符串的情况,这个可以通过一个简单的保护措施来处理。

我在 Python 3.8 中测试过,但大概从 3.4 版本开始就能正常工作(在一些旧版本中,len() 计算的是代码单元而不是代码点,我猜如果字符串中包含 BMP 以外的字符,可能会出问题)。

撰写回答