这似乎应该是非常简单的,但我是Python的新手,并希望以最恐怖的方式来做。
我想在字符串中找到第n个子字符串。
必须有一些与我想做的事情相同的东西
mystring.find("substring", 2nd)
如何在Python中实现这一目标?
答案 0 :(得分:60)
这是一个更直接的迭代解决方案的Pythonic版本:
def find_nth(haystack, needle, n):
start = haystack.find(needle)
while start >= 0 and n > 1:
start = haystack.find(needle, start+len(needle))
n -= 1
return start
示例:的
>>> find_nth("foofoofoofoo", "foofoo", 2)
6
如果您想找到needle
的第n个重叠出现,您可以按1
而不是len(needle)
递增,如下所示:
def find_nth_overlapping(haystack, needle, n):
start = haystack.find(needle)
while start >= 0 and n > 1:
start = haystack.find(needle, start+1)
n -= 1
return start
示例:的
>>> find_nth_overlapping("foofoofoofoo", "foofoo", 2)
3
这比Mark的版本更容易阅读,并且它不需要分割版本的额外内存或导入正则表达式模块。它还遵循Zen of python中的一些规则,与各种re
方法不同:
答案 1 :(得分:55)
马克的迭代方法是常用的方法,我认为。
以下是字符串拆分的替代方法,通常可用于查找相关流程:
def findnth(haystack, needle, n):
parts= haystack.split(needle, n+1)
if len(parts)<=n+1:
return -1
return len(haystack)-len(parts[-1])-len(needle)
这是一个快速(有点脏,因为你必须选择一些与针不匹配的箔条)单线:
'foo bar bar bar'.replace('bar', 'XXX', 1).find('bar')
答案 2 :(得分:30)
这将在字符串中找到第二次出现的子字符串。
def find_2nd(string, substring):
return string.find(substring, string.find(substring) + 1)
编辑:我对性能没有太多考虑,但快速递归可以帮助找到第n次出现:
def find_nth(string, substring, n):
if (n == 1):
return string.find(substring)
else:
return string.find(substring, find_nth(string, substring, n - 1) + 1)
答案 3 :(得分:19)
了解正则表达式并不总是最好的解决方案,我可能会在这里使用一个:
>>> import re
>>> s = "ababdfegtduab"
>>> [m.start() for m in re.finditer(r"ab",s)]
[0, 2, 11]
>>> [m.start() for m in re.finditer(r"ab",s)][2] #index 2 is third occurrence
11
答案 4 :(得分:17)
我提供了一些基准测试结果,比较了迄今为止最突出的方法,即@ bobince的findnth()
(基于str.split()
)与@ tgamblin或@Mark Byers'find_nth()
(基于str.find()
)。我还将与C扩展(_find_nth.so
)进行比较,看看我们能走得多快。这是find_nth.py
:
def findnth(haystack, needle, n):
parts= haystack.split(needle, n+1)
if len(parts)<=n+1:
return -1
return len(haystack)-len(parts[-1])-len(needle)
def find_nth(s, x, n=0, overlap=False):
l = 1 if overlap else len(x)
i = -l
for c in xrange(n + 1):
i = s.find(x, i + l)
if i < 0:
break
return i
当然,如果字符串很大,性能最重要,所以假设我们想在名为'bigfile'的1.3 GB文件中找到1000001st换行符('\ n')。为了节省内存,我们希望处理文件的mmap.mmap
对象表示:
In [1]: import _find_nth, find_nth, mmap
In [2]: f = open('bigfile', 'r')
In [3]: mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
findnth()
已存在第一个问题,因为mmap.mmap
个对象不支持split()
。所以我们实际上必须将整个文件复制到内存中:
In [4]: %time s = mm[:]
CPU times: user 813 ms, sys: 3.25 s, total: 4.06 s
Wall time: 17.7 s
哎哟!幸运的是s
仍然适合我的Macbook Air的4 GB内存,所以让我们的基准findnth()
:
In [5]: %timeit find_nth.findnth(s, '\n', 1000000)
1 loops, best of 3: 29.9 s per loop
显然表现糟糕。让我们看看基于str.find()
的方法是如何做到的:
In [6]: %timeit find_nth.find_nth(s, '\n', 1000000)
1 loops, best of 3: 774 ms per loop
好多了!很明显,findnth()
的问题是它被强制在split()
期间复制字符串,这已经是我们第二次在s = mm[:]
之后复制1.3 GB的数据了。这里有find_nth()
的第二个优势:我们可以直接在mm
上使用它,这样就需要零副本文件:
In [7]: %timeit find_nth.find_nth(mm, '\n', 1000000)
1 loops, best of 3: 1.21 s per loop
mm
与s
的效果似乎很小,但这表明find_nth()
可以在1.2秒内得到答案,与findnth
相比总共47秒。
我发现没有基于str.find()
的方法明显比基于str.split()
的方法差的情况,所以在这一点上,我认为应该接受@ tgamblin或@Mark Byers的答案@ bobince的。
在我的测试中,上面的find_nth()
版本是我能想到的最快的纯Python解决方案(非常类似于@Mark Byers的版本)。让我们看看我们可以用C扩展模块做得更好。这是_find_nthmodule.c
:
#include <Python.h>
#include <string.h>
off_t _find_nth(const char *buf, size_t l, char c, int n) {
off_t i;
for (i = 0; i < l; ++i) {
if (buf[i] == c && n-- == 0) {
return i;
}
}
return -1;
}
off_t _find_nth2(const char *buf, size_t l, char c, int n) {
const char *b = buf - 1;
do {
b = memchr(b + 1, c, l);
if (!b) return -1;
} while (n--);
return b - buf;
}
/* mmap_object is private in mmapmodule.c - replicate beginning here */
typedef struct {
PyObject_HEAD
char *data;
size_t size;
} mmap_object;
typedef struct {
const char *s;
size_t l;
char c;
int n;
} params;
int parse_args(PyObject *args, params *P) {
PyObject *obj;
const char *x;
if (!PyArg_ParseTuple(args, "Osi", &obj, &x, &P->n)) {
return 1;
}
PyTypeObject *type = Py_TYPE(obj);
if (type == &PyString_Type) {
P->s = PyString_AS_STRING(obj);
P->l = PyString_GET_SIZE(obj);
} else if (!strcmp(type->tp_name, "mmap.mmap")) {
mmap_object *m_obj = (mmap_object*) obj;
P->s = m_obj->data;
P->l = m_obj->size;
} else {
PyErr_SetString(PyExc_TypeError, "Cannot obtain char * from argument 0");
return 1;
}
P->c = x[0];
return 0;
}
static PyObject* py_find_nth(PyObject *self, PyObject *args) {
params P;
if (!parse_args(args, &P)) {
return Py_BuildValue("i", _find_nth(P.s, P.l, P.c, P.n));
} else {
return NULL;
}
}
static PyObject* py_find_nth2(PyObject *self, PyObject *args) {
params P;
if (!parse_args(args, &P)) {
return Py_BuildValue("i", _find_nth2(P.s, P.l, P.c, P.n));
} else {
return NULL;
}
}
static PyMethodDef methods[] = {
{"find_nth", py_find_nth, METH_VARARGS, ""},
{"find_nth2", py_find_nth2, METH_VARARGS, ""},
{0}
};
PyMODINIT_FUNC init_find_nth(void) {
Py_InitModule("_find_nth", methods);
}
以下是setup.py
文件:
from distutils.core import setup, Extension
module = Extension('_find_nth', sources=['_find_nthmodule.c'])
setup(ext_modules=[module])
照常安装python setup.py install
。 C代码在这里发挥优势,因为它仅限于查找单个字符,但让我们看看它有多快:
In [8]: %timeit _find_nth.find_nth(mm, '\n', 1000000)
1 loops, best of 3: 218 ms per loop
In [9]: %timeit _find_nth.find_nth(s, '\n', 1000000)
1 loops, best of 3: 216 ms per loop
In [10]: %timeit _find_nth.find_nth2(mm, '\n', 1000000)
1 loops, best of 3: 307 ms per loop
In [11]: %timeit _find_nth.find_nth2(s, '\n', 1000000)
1 loops, best of 3: 304 ms per loop
显然还是要快一点。有趣的是,内存和mmapped案例之间的C级别没有差异。同样有趣的是,基于_find_nth2()
的{{1}}库函数的string.h
对memchr()
中的直接实现失去了作用:额外的“优化”在_find_nth()
显然是在适得其反......
总之,memchr()
(基于findnth()
)的实现确实是一个坏主意,因为(a)由于所需的复制,它对较大的字符串执行非常糟糕,并且(b)
它根本不适用于str.split()
个对象。在mmap.mmap
(基于find_nth()
)中的实施应该在所有情况下都是首选(因此是该问题的可接受答案)。
仍有相当大的改进空间,因为C扩展比纯Python代码快了近4倍,这表明可能存在专用Python库函数的情况。
答案 5 :(得分:6)
我可能会做这样的事情,使用带有索引参数的find函数:
def find_nth(s, x, n):
i = -1
for _ in range(n):
i = s.find(x, i + len(x))
if i == -1:
break
return i
print find_nth('bananabanana', 'an', 3)
我猜这不是特别Pythonic,但它很简单。你可以用递归代替它:
def find_nth(s, x, n, i = 0):
i = s.find(x, i)
if n == 1 or i == -1:
return i
else:
return find_nth(s, x, n - 1, i + len(x))
print find_nth('bananabanana', 'an', 3)
这是解决它的一种功能性方法,但我不知道这是否会使它更像Pythonic。
答案 6 :(得分:5)
最简单的方法?
text = "This is a test from a test ok"
firstTest = text.find('test')
print text.find('test', firstTest + 1)
答案 7 :(得分:2)
这是另一个re
+ itertools
版本,在搜索str
或RegexpObject
时应该有效。我会自由地承认,这可能是过度设计的,但出于某种原因,它让我很开心。
import itertools
import re
def find_nth(haystack, needle, n = 1):
"""
Find the starting index of the nth occurrence of ``needle`` in \
``haystack``.
If ``needle`` is a ``str``, this will perform an exact substring
match; if it is a ``RegexpObject``, this will perform a regex
search.
If ``needle`` doesn't appear in ``haystack``, return ``-1``. If
``needle`` doesn't appear in ``haystack`` ``n`` times,
return ``-1``.
Arguments
---------
* ``needle`` the substring (or a ``RegexpObject``) to find
* ``haystack`` is a ``str``
* an ``int`` indicating which occurrence to find; defaults to ``1``
>>> find_nth("foo", "o", 1)
1
>>> find_nth("foo", "o", 2)
2
>>> find_nth("foo", "o", 3)
-1
>>> find_nth("foo", "b")
-1
>>> import re
>>> either_o = re.compile("[oO]")
>>> find_nth("foo", either_o, 1)
1
>>> find_nth("FOO", either_o, 1)
1
"""
if (hasattr(needle, 'finditer')):
matches = needle.finditer(haystack)
else:
matches = re.finditer(re.escape(needle), haystack)
start_here = itertools.dropwhile(lambda x: x[0] < n, enumerate(matches, 1))
try:
return next(start_here)[1].start()
except StopIteration:
return -1
答案 8 :(得分:1)
>>> s="abcdefabcdefababcdef"
>>> j=0
>>> for n,i in enumerate(s):
... if s[n:n+2] =="ab":
... print n,i
... j=j+1
... if j==2: print "2nd occurence at index position: ",n
...
0 a
6 a
2nd occurence at index position: 6
12 a
14 a
答案 9 :(得分:1)
这是另一种使用re.finditer的方法 不同之处在于,只有在必要时才能看到草垛
from re import finditer
from itertools import dropwhile
needle='an'
haystack='bananabanana'
n=2
next(dropwhile(lambda x: x[0]<n, enumerate(re.finditer(needle,haystack))))[1].start()
答案 10 :(得分:1)
这将为您提供一系列与yourstring
匹配的起始索引:
import re
indices = [s.start() for s in re.finditer(':', yourstring)]
然后你的第n个条目是:
n = 2
nth_entry = indices[n-1]
当然,您必须小心索引边界。您可以像这样获取yourstring
的实例数:
num_instances = len(indices)
答案 11 :(得分:1)
以 modle13 的答案为基础,但没有re
模块依赖。
def iter_find(haystack, needle):
return [i for i in range(0, len(haystack)) if haystack[i:].startswith(needle)]
我希望这是一个内置的字符串方法。
>>> iter_find("http://stackoverflow.com/questions/1883980/", '/')
[5, 6, 24, 34, 42]
答案 12 :(得分:1)
# return -1 if nth substr (0-indexed) d.n.e, else return index
def find_nth(s, substr, n):
i = 0
while n >= 0:
n -= 1
i = s.find(substr, i + 1)
return i
答案 13 :(得分:0)
以防万一有人想从后面找到第 n 个:
def find_nth_reverse(haystack: str, needle: str, n: int) -> int:
end = haystack.rfind(needle)
while end >= 0 and n > 1:
end = haystack.rfind(needle, 0, end - len(needle))
n -= 1
return end
答案 14 :(得分:0)
这是一个简单而有趣的方法:
def index_of_nth(text, substring, n) -> int:
index = 0
for _ in range(n):
index = text.index(substring, index) + 1
return index - 1
答案 15 :(得分:0)
当所提供的发生输入值大于实际发生次数时,请避免出现故障或输出错误。例如,在字符串'overflow'中,如果您要检查'o'的第3次出现(它只有2次出现),则下面的代码将返回警告或消息,指示已超过出现次数。
def check_nth_occurrence (string, substr, n):
## Count the Occurrence of a substr
cnt = 0
for i in string:
if i ==substr:
cnt = cnt + 1
else:
pass
## Check if the Occurrence input has exceeded the actual count of Occurrence
if n > cnt:
print (f' Input Occurrence entered has exceeded the actual count of Occurrence')
return
## Get the Index value for first Occurrence of the substr
index = string.find(substr)
## Get the Index value for nth Occurrence of Index
while index >= 0 and n > 1:
index = string.find(substr, index+ 1)
n -= 1
return index
答案 16 :(得分:0)
Def:
def get_first_N_words(mytext, mylen = 3):
mylist = list(mytext.split())
if len(mylist)>=mylen: return ' '.join(mylist[:mylen])
要使用:
get_first_N_words(' One Two Three Four ' , 3)
输出:
'One Two Three'
答案 17 :(得分:0)
对于特殊情况,当您搜索字符的第n个出现位置(即长度为1的子字符串)时,以下功能通过构建给定字符出现的所有位置的列表来起作用:
def find_char_nth(string, char, n):
"""Find the n'th occurence of a character within a string."""
return [i for i, c in enumerate(string) if c == char][n-1]
如果给定字符的出现次数少于n
,则将给出IndexError: list index out of range
。
这是从@Zv_oDD的answer派生而来的,并且对于单个字符进行了简化。
答案 18 :(得分:0)
这是我在字符串n
中查找b
第a
个出现的解决方案:
from functools import reduce
def findNth(a, b, n):
return reduce(lambda x, y: -1 if y > x + 1 else a.find(b, x + 1), range(n), -1)
这是纯Python并且是迭代的。对于0或n
太大,它将返回-1。它是单线的,可以直接使用。这是一个示例:
>>> reduce(lambda x, y: -1 if y > x + 1 else 'bibarbobaobaotang'.find('b', x + 1), range(4), -1)
7
答案 19 :(得分:0)
不使用循环和递归的解决方案。
在编译方法中使用所需的模式并输入所需的 出现在变量'n'中,最后一条语句将显示 给定模式中第n次出现的开始索引 串。这是finditer的结果,即迭代器正在转换 列出并直接访问第n个索引。
import re
n=2
sampleString="this is history"
pattern=re.compile("is")
matches=pattern.finditer(sampleString)
print(list(matches)[n].span()[0])
答案 20 :(得分:0)
这是你真正想要的答案:
def Find(String,ToFind,Occurence = 1):
index = 0
count = 0
while index <= len(String):
try:
if String[index:index + len(ToFind)] == ToFind:
count += 1
if count == Occurence:
return index
break
index += 1
except IndexError:
return False
break
return False
答案 21 :(得分:0)
提供另一个&#34;棘手&#34;解决方案,使用split
和join
。
在您的示例中,我们可以使用
len("substring".join([s for s in ori.split("substring")[:2]]))
答案 22 :(得分:0)
替换一个衬垫很棒,但只能工作,因为XX和杆具有相同的长度
一个好的和一般的def将是:
def findN(s,sub,N,replaceString="XXX"):
return s.replace(sub,replaceString,N-1).find(sub) - (len(replaceString)-len(sub))*(N-1)
答案 23 :(得分:-1)
怎么样:
c = os.getcwd().split('\\')
print '\\'.join(c[0:-2])