我们说我有以下字符串:
"my ., .,dog. .jumps. , .and..he. .,is., .a. very .,good, .dog"
1234567890123456789012345678901234567890123456789012345678901 <-- char pos
现在,我编写了一个正则表达式来删除上面字符串中的某些元素,在本例中,是所有空格,所有句点和所有逗号。
我留下了以下转换后的字符串:
"mydogjumpsandheisaverygooddog"
现在,我想构造这个字符串的k-gram。让我们说我要拿5克上面的字符串,它看起来像:
mydog ydogj dogju ogjum gjump jumps umpsa ...
我遇到的问题是,对于每个k-gram,我想在我列出的第一个源文本中跟踪其原始字符位置。
因此,“mydog”的起始位置为“0”,结束位置为“11”。但是,我在源文本和修改后的文本之间没有映射。所以,我不知道特定的k-gram在原始未经修改的文本中的开始和结束位置。这对我的程序来说很重要。
我正在创建一个像这样的k-gram列表:
public class Kgram
{
public int start;
public int end;
public int text;
}
其中start
和end
是源文本中的位置(顶部),文本是修改后的k-gram文本。
有人能指出我正确的方向来解决这个问题吗?
答案 0 :(得分:5)
请勿使用正则表达式'replace'API进行替换。仅使用regexp查找要修改的位置,自己执行mod,并维护偏移映射。我使用的一种形式是一个与原始字符串一样大的int数组,在这里存储'n chars deleted'值,但还有许多其他的可能性。
这里的基本数据结构是一对数组。每对包含偏移和校正。根据时间/空间的权衡,您可能更愿意将信息传播到与原始字符串一样大的数据结构上。
答案 1 :(得分:5)
以下是我在Haskell中解决这个问题的方法:
kgramify k string =
let charsWithPos = zip string [1..] -- attach original position to each char
goodCWP = filter (not o isWhitePeriodOrComma o fst) charsWithPos -- drop nasty chars
groups = takeEveryK k goodCWP -- clump remaining chars in groups of size k
posnOfGroup g = (snd (head g), map fst g) -- position of first char with group
in map posnOfGroup groups
非正式英语:
k
在任何功能语言中,如Clean,Haskell,ML或Scheme,这种事情都很容易。在具有显式内存分配(new
)或更差,malloc
和free
的语言中,这样的解决方案将非常繁琐。
答案 2 :(得分:2)
一个C解决方案,表明正如诺曼拉姆齐所说,这是非常繁琐的。它将过滤器作为带有上下文的回调,仅用于踢,但在您的情况下,您可以将0
作为过滤器数据并将not_wspc
作为过滤器传递:
int not_wspc(void *, char c) {
if isspace((unsigned char)c) return 0;
if ((c == '.') || (c == ',')) return 0;
return 1;
}
typedef struct {
char c;
int pos;
} charwithpos;
KGram *foo(const char *input, int (*filter)(void *,char), void *filterdata) {
size_t len = strlen(input);
charwithpos *filtered = malloc(len * sizeof(*filtered));
assert(filtered);
// combine Norman's zip and filter steps
charwithpos *current = filtered
for (size_t i = 0; i < len; ++i) {
if (filter(filterdata, input[i])) {
current->c = input[i];
current->pos = i;
++current;
}
}
size_t shortlen = (current - filtered);
// wouldn't normally recommend returning malloced data, but
// illustrates the point.
KGram *result = malloc((shortlen / 5 + 1) * sizeof(*result));
assert(result);
// take each 5 step
KGram *currentgram = result;
current = filtered;
for (size_t i = 0; i < shortlen; ++i) {
currentgram->text[i%5] = current->c;
if ((i % 5) == 0) {
currentgram->start = current->pos;
} else if ((i % 5) == 4) {
currentgram->end = current->pos;
++currentgram;
}
++current;
}
if (shortlen % 5) != 0 {
currentgram->end = filtered[shortlen-1].pos;
currentgram->text[shortlen%5] = 0;
}
free(filtered);
return(result);
}
或类似的东西,我实际上无法编译和测试它。显然,这有一个显着的弱点,filtered
一次看到一个字符,这意味着它不能应用回溯算法。你可以通过将整个字符串传递给过滤器来绕过它,这样如果有必要,它可以在第一次调用时做很多工作,并存储结果以返回所有其余的调用。但是如果你需要将类似正则表达式的逻辑应用于任意类型,那么C可能不是正确的语言。
这是C ++解决方案的开端,甚至没有使用<functional>
。不确定Norman对new
语言的评价:仅仅因为语言并不意味着你必须使用它; - )
template <typename OutputIterator>
struct KGramOutput {
OutputIterator dest;
KGram kgram;
KGramOutput(OutputIterator dest) : dest(dest) {}
void add(char, size_t);
void flush(void);
};
template <typename InputIterator, typename OutputIterator, typename Filter>
void foo(InputIterator first, InputIterator last, OutputIterator dest, Filter filter) {
size_t i = 0;
KGramOutput<OutputIterator> kgram(dest);
while (first != last) {
if (filter(*first)) kgram.add(*first, i);
++first;
++i;
}
kgram.flush();
}
add
和flush
函数有点乏味,他们必须将5对捆绑到KGram结构中,然后执行*dest++ = kgram
。用户可以通过pushback_iterator
作为输出迭代器传递vector<KGram>
。顺便说一下'5'和'char'也可以是模板参数。
答案 3 :(得分:1)
这可以一次完成,无需构建中间字符位置对:
(defclass k-gram ()
((start :reader start :initarg :start)
(end :accessor end)
(text :accessor text)))
(defmethod initialize-instance :after ((k-gram k-gram) &rest initargs &key k)
(declare (ignorable initargs))
(setf (slot-value k-gram 'text) (make-array k :element-type 'character)))
(defun k-gramify (string k ignore-string)
"Builds the list of complete k-grams with positions from the original
text, but with all characters in ignore-string ignored."
(loop
for character across string
for position upfrom 0
with k-grams = ()
do (unless (find character ignore-string)
(push (make-instance 'k-gram :k k :start position) k-grams)
(loop
for k-gram in k-grams
for i upfrom 0 below k
do (setf (aref (text k-gram) i) character
(end k-gram) (1+ position))))
finally (return (nreverse (nthcdr (- k 1) k-grams)))))