Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/216.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 有人能推荐一种更快的替代正则表达式算法吗?_Java_Android_Replace - Fatal编程技术网

Java 有人能推荐一种更快的替代正则表达式算法吗?

Java 有人能推荐一种更快的替代正则表达式算法吗?,java,android,replace,Java,Android,Replace,我需要经常调用这个函数。基本上,它将所有重音字符替换为非重音的等效字符,将空格改为“u”,转换为小写,并去除其余的外来代码,因此可以安全地用作文件名/url路径等。。问题是,正如您所看到的,从性能的角度来看,它看起来很糟糕。有人能想出一个更干净、更快的替代方案吗 public static String makeValidPathName(String rawString) { if (rawString==null) return null; rawString = rawSt

我需要经常调用这个函数。基本上,它将所有重音字符替换为非重音的等效字符,将空格改为“u”,转换为小写,并去除其余的外来代码,因此可以安全地用作文件名/url路径等。。问题是,正如您所看到的,从性能的角度来看,它看起来很糟糕。有人能想出一个更干净、更快的替代方案吗

public static String makeValidPathName(String rawString) {
    if (rawString==null) return null;
    rawString = rawString.replaceAll("[ÁÀÂÄáàäaàáâãäå]","a");
    rawString = rawString.replaceAll("æ","ae");
    rawString = rawString.replaceAll("çÇ","c");
    rawString = rawString.replaceAll("[ÈÉÊËèéêë]","e");
    rawString = rawString.replaceAll("[ìíîïÍÌÎÏ]","i");
    rawString = rawString.replaceAll("ñÑ","n");                            
    rawString = rawString.replaceAll("[ÓÓÖÔòóôõö]","o");
    rawString = rawString.replaceAll("œ","oe");
    rawString = rawString.replaceAll("[ÙÚÛÜùúûü]", "u");
    rawString = rawString.replaceAll("[ýÿ]","y");
    rawString= rawString.replaceAll("[^a-z A-Z 0-9 \\_ \\+]","");
    rawString = rawString.replaceAll(" ","_");
    return rawString.toLowerCase();
}
---编辑

好的,伙计们。。。我对所有4种情况进行了性能测试:

  • 案例1)发布在此处的原始例程
  • 案例2)@WChargin建议的改进
  • 案例3)@devconsole建议的查找表以及我对SparseArray的优化
  • 案例4)@Erik Pragt建议的规范化方法
而且。。。获胜者是。。。TADAAA

D/REPLACEMENT_TEST(18563): *** Running Tests (1000 iterations)
D/REPLACEMENT_TEST(18563): Original REGEX  : 13533 ms
D/REPLACEMENT_TEST(18563): Compiled REGEX  : 12563 ms
D/REPLACEMENT_TEST(18563): Character LUT   : 1840 ms
D/REPLACEMENT_TEST(18563): Java NORMALIZER : 2416 ms
  • 有趣的是,模式编译优化没有多大帮助
  • 我发现我对正则表达式速度的假设也是完全错误的,而devconsole对规范化器优于正则表达式的猜测是正确的
  • 正则表达式的速度之慢令人惊讶。一个数量级的差异真的让我吃惊。我会尽量避免在Java上使用它们
  • 查找表是最快的短边距选项。我将坚持使用这个解决方案,因为使用规范化器,我仍然需要手动替换一些字符(将空格替换为“ux”),然后转换为小写
测试是在三星Galaxy Tab v1 10.1中完成的

请参阅附件中测试用例的源代码

public class Misc { 

    /* Test 2 (@WCChargin's Regex compilation) initialization */

static Map<Pattern, String> patterns = new HashMap<Pattern, String>();

static {
        patterns.put(Pattern.compile("[ÁÀÂÄáàäaàáâãäå]") ,"a");
        patterns.put(Pattern.compile("æ"),"ae");
        patterns.put(Pattern.compile("çÇ"),"c");
        patterns.put(Pattern.compile("[ÈÉÊËèéêë]"),"e");
        patterns.put(Pattern.compile("[ìíîïÍÌÎÏ]"),"i");
        patterns.put(Pattern.compile("ñÑ"),"n");                            
        patterns.put(Pattern.compile("[ÓÓÖÔòóôõö]"),"o");
        patterns.put(Pattern.compile("œ"),"oe");
        patterns.put(Pattern.compile("[ÙÚÛÜùúûü]"), "u");
        patterns.put(Pattern.compile("[ýÿ]"),"y");
        patterns.put(Pattern.compile("[^a-z A-Z 0-9 \\_ \\+]"),"");
        patterns.put(Pattern.compile(" "),"_");
}

    /* Test 3 (@devconsole's Lookup table) initialization */
static SparseArray<String> homeBrewPatterns=new SparseArray<String>();
/** helper function to fill the map where many different chars map to the same replacement */
static void fillMap(String chars, String replacement) { for (int i=0,len=chars.length(); i<len; i++) homeBrewPatterns.put(chars.charAt(i), replacement); }
static {
    // fill the sparsearray map with all possible substitutions: Any char code gets its equivalent, ie, ä->a. a->a. A->a
    // this also does the toLowerCase automatically. If a char is not in the list, it is forbidden and we skip it.
    fillMap("ÁÀÂÄáàäaàáâãäå","a");
    fillMap("æ","ae");
    fillMap("çÇ","c");
    fillMap("ÈÉÊËèéêë","e");
    fillMap("ìíîïÍÌÎÏ","i");
    fillMap("ñÑ","n");
    fillMap("ÓÓÖÔòóôõö","o");
    fillMap("œ","oe");
    fillMap("ÙÚÛÜùúûü","u");
    fillMap("ýÿ","y");
    fillMap(" ","_");
    for (char c='a'; c<='z'; c++) fillMap(""+c,""+c); // fill standard ASCII lowercase -> same letter 
    for (char c='A'; c<='Z'; c++) fillMap(""+c,(""+c).toLowerCase()); // fill standard ASCII uppercase -> uppercase
    for (char c='0'; c<='9'; c++) fillMap(""+c,""+c); // fill numbers
}

    /** FUNCTION TO TEST #1: Original function, no pattern compilation */ 
public static String makeValidPathName(String rawString) {
    if (rawString==null) return null;
    rawString = rawString.replaceAll("[ÁÀÂÄáàäaàáâãäå]","a");
    rawString = rawString.replaceAll("æ","ae");
    rawString = rawString.replaceAll("çÇ","c");
    rawString = rawString.replaceAll("[ÈÉÊËèéêë]","e");
    rawString = rawString.replaceAll("[ìíîïÍÌÎÏ]","i");
    rawString = rawString.replaceAll("ñÑ","n");                            
    rawString = rawString.replaceAll("[ÓÓÖÔòóôõö]","o");
    rawString = rawString.replaceAll("œ","oe");
    rawString = rawString.replaceAll("[ÙÚÛÜùúûü]", "u");
    rawString = rawString.replaceAll("[ýÿ]","y");
    rawString = rawString.replaceAll("[^a-z A-Z 0-9 \\_ \\+]","");
    rawString = rawString.replaceAll(" ","_");
    return rawString.toLowerCase();
}
/** FUNCTION TO TEST #2: @WCChargin's suggestion: Compile patterns then iterate a map */
public static String makeValidPathName_compiled(String rawString) {
    for (Map.Entry<Pattern, String> e : patterns.entrySet()) {
        rawString=e.getKey().matcher(rawString).replaceAll(e.getValue());
    }
    return rawString.toLowerCase();
}

    /** FUNCTION TO TEST #3: @devconsole's suggestion: Create a LUT with all equivalences then append to a stringbuilder */
public static String makeValidPathName_lut(String rawString) {
    StringBuilder purified=new StringBuilder(rawString.length()); // to avoid resizing
    String aux; // to avoid creating objects inside the for
    for (int i=0, len=rawString.length(); i<len; i++) {
        aux=homeBrewPatterns.get(rawString.charAt(i));
        if (aux!=null) purified.append(aux);
    }
    return purified.toString();
}

    /** FUNCTION TO TEST #4: @Erik Pragt's suggestion on the use of a Normalizer */
public static String makeValidPathName_normalizer(String rawString) {
        return rawString == null ? null
            : Normalizer.normalize(rawString, Form.NFD)
                .replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}

    /** Test Runner as a Runnable, just do a Handler.post() to run it */

public static Runnable msStringReplacementTest=new Runnable() {

    public void run() {


    String XTAG="REPLACEMENT_TEST";

    Log.d(XTAG, "*** Running Tests");

    int ITERATIONS=1000;

    String[] holder=new String[4];

            /* http://www.adhesiontext.com/ to generate dummy long text ... its late n im tired :) */

    String tester="e arte nací valse ojales i demediada cesé entrañan domó reo ere fiaréis cinesiterapia fina pronto mensuraré la desatufases adulantes toree fusca ramiro hez apolíneo insalvable atas no enorme mí ojo trola chao fas eh borda no consignataria uno ges no arenque ahuyento y daca pío veló tenle baúl ve birria filo rho fui tañe mean taz raicita alimentarías ano defunciones u reabráis repase apreciaran cantorales ungidas naftalina ex guió abomba o escribimos consultarás des usó saudí mercó yod aborrecieses guiri lupia ña adosado jeringara fe cabalgadura tú descasar diseñe amar limarme escobero latamente e oreó lujuria niñez fabularios da inviernen vejé estañarán dictará sil mírales emoción zar claudiquéis ó e ti ñ veintén dañen ríase esmeraras acató noté as mancharlos avisen chocarnos divertidas y relata nuera usé fié élitro baba upa cu enhornan da toa hechizase genesíacos sol fija aplicó gafa pi enes fin asé deal rolar recurran cacen ha id pis pisó democristiano oes eras lañó ch pino fijad ñ quita hondazos ñ determinad vid corearan corrompimiento pamema meso fofas ocio eco amagados pian bañarla balan cuatrimestrales pijojo desmandara merecedor nu asimiladores denunciándote jada ñudos por descifraseis oré pelote ro botó tu sí mejorado compatibilizaciones enyerba oyeses atinado papa borbón pe dé ñora semis prosada luces leí aconteciesen doy colmará o ve te modismos virola garbillen apostabas abstenido ha bajá le osar cima ají adormecéis ñu mohecí orden abrogándote dan acanilladas uta emú ha emporcara manila arribeña hollejo ver puntead ijadeáis chalanesca pechugón silbo arabescos e i o arenar oxidas palear ce oca enmaderen niñez acude topó aguanieves i aconsejaseis lago él roía grafito ceñir jopo nitritos mofe botáis nato compresores ñu asilo amerizan allanándola cuela ó han ice puya alta lío son de sebo antieconómicas alá aceza latitud faca lavé colocándolos concebirlo miserea ñus gro mearé enchivarse";

    long time0=System.currentTimeMillis();
    for (int i=0; i<ITERATIONS; i++) holder[0]=makeValidPathName(tester); // store in an array to avoid possible JIT optimizations
    long elapsed0=System.currentTimeMillis()-time0;

    Log.d(XTAG, "Original REGEX  : "+elapsed0+" ms");

    long time1=System.currentTimeMillis();
    for (int i=0; i<ITERATIONS; i++) holder[1]=makeValidPathName_compiled(tester); // store in an array to avoid possible JIT optimizations
    long elapsed1=System.currentTimeMillis()-time1;

    Log.d(XTAG, "Compiled REGEX  : "+elapsed1+" ms");

    long time2=System.currentTimeMillis();
    for (int i=0; i<ITERATIONS; i++) holder[2]=makeValidPathName_lut(tester); // store in an array to avoid possible JIT optimizations
    long elapsed2=System.currentTimeMillis()-time2;

    Log.d(XTAG, "Character LUT   : "+elapsed2+" ms");

    long time3=System.currentTimeMillis();
    for (int i=0; i<ITERATIONS; i++) holder[3]=makeValidPathName_normalizer(tester); // store in an array to avoid possible JIT optimizations
    long elapsed3=System.currentTimeMillis()-time3;

    Log.d(XTAG, "Java NORMALIZER : "+elapsed3+" ms");

    Log.d(XTAG, "Output 0: "+holder[0]);
    Log.d(XTAG, "Output 1: "+holder[1]);
    Log.d(XTAG, "Output 2: "+holder[2]);
    Log.d(XTAG, "Output 3: "+holder[3]);
    }
};
公共类杂项{
/*测试2(@WCChargin的正则表达式编译)初始化*/
静态映射模式=新的HashMap();
静止的{
patterns.put(Pattern.compile(Pattern.compile)(“a”),“a”);
patterns.put(Pattern.compile(“æ”),“ae”);
patterns.put(Pattern.compile,c);
patterns.put(Pattern.compile(“[ÈÊËèèèè]”),e);
patterns.put(Pattern.compile(“i”);
patterns.put(Pattern.compile(“ññ”),“n”);
patterns.put(Pattern.compile(“Pattern.compile”),“o”);
patterns.put(Pattern.compile(“œ”),“oe”);
patterns.put(Pattern.compile(“[ÙÚÛÜùúúü]”,“u”);
patterns.put(Pattern.compile(“[ýÿ]”),“y”);
patterns.put(Pattern.compile(“[^a-za-z0-9\\\\\\\+]”);
patterns.put(Pattern.compile(“”,“)”);
}
/*测试3(@devconsole的查找表)初始化*/
静态SparseArray homeBrewPatterns=新SparseArray();
/**helper函数用于填充多个不同字符映射到同一替换的映射*/
静态void fillMap(字符串字符,字符串替换){for(inti=0,len=chars.length();ia.a->a.a->a
//这也会自动执行toLowerCase。如果一个字符不在列表中,它将被禁止,我们将跳过它。
fillMap(“a”);
填充图(“æ”、“ae”);
fillMap(“çç”,“c”);
fillMap(“e”);
fillMap(“i”);
fillMap(“ññ”,“n”);
fillMap(“o”);
fillMap(“œ”、“oe”);
fillMap(“u”);
fillMap(“ýÿ”、“y”);
填充图(“,”);
对于(char c='a';c)同一个字母
for(char c='A';c大写)

对于(char c='0';c您可以尝试使用a来代替正则表达式。您可以找到有关它的信息

从该页:

import java.text.Normalizer;
import java.text.Normalizer.Form;

// ...

public static String removeAccents(String text) {
    return text == null ? null
        : Normalizer.normalize(text, Form.NFD)
            .replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}

它不能解决您的所有需求,但它适用于相当广泛的字符范围。然而,我建议您进行性能测量,以比较这两种解决方案。如果您真的想使用正则表达式解决方案,请尝试先创建一些静态模式,然后使用匹配器,这样您只会得到创建的开销重新编译这些模式一次。

如果必须使用regex,可以预编译模式:

Map<Pattern, String> patterns = new HashMap<Pattern, String>();
{ // initializer block (you could do this in constructor also)
    patterns.put(Pattern.compile("[ÁÀÂÄáàäaàáâãäå]") ,"a");
    rawString = rawString.replaceAll("æ","ae");
    // etc.
}

// later...
for (Map.Entry<Pattern, String> e : patterns) {
    rawString = e.getValue().matcher(rawString).replaceAll(e.getKey());
}
Map patterns=newhashmap();
{//初始值设定项块(也可以在构造函数中执行此操作)
patterns.put(Pattern.compile(Pattern.compile)(“a”),“a”);
rawString=rawString.replaceAll(“æ”、“ae”);
//等等。
}
//后来。。。
用于(映射条目e:模式){
rawString=e.getValue().matcher(rawString).replaceAll(e.getKey());
}
for
循环的
行是关键。下面是剖析:

  • e.getValue()
    从映射键获取模式
  • .matcher(rawString)
    获取模式的
    matcher
    对象,以将正则表达式的实例与给定字符串(原始字符串)匹配
  • .replaceAll
    的工作原理与
    String
    方法一样(我相信
    String
    实际上使用了这个方法)
  • e.getKey()
    从映射键获取要替换的值

进一步阅读的链接:

  • 及其方法
构建一个静态
映射
,该映射将字符映射到其替换字符串,即将“Á”映射到“a”,将“ä”映射到“a”,等等。还包括一对一的对应关系,即将“a”映射到“a”,等等。您得到的映射最多包含几百个条目


现在迭代输入字符串的字符,并在静态映射中查找替换字符串。如果映射不包含条目,请跳过该字符,否则将替换内容附加到StringBuilder。

Oohh!!看起来这正是我要查找的!您认为它的性能会比上面的更好吗?:-).没问题。好吧,这就是为什么我建议首先创建一个基准,如果你真的关心性能的话。我不会为它操心太多,而编写一个微基准可能会很棘手。我需要在紧循环中调用这个函数数百次,在我的情况下,一个基准可能是值得的…无论如何,我选择c因为这样我可以一次完成所有的替换,而不是o