如何截断elixir中的字符串?

时间:2016-09-08 15:27:58

标签: elixir

我正在使用slu for elixir,我的想法是:我有一个字符串,其中string = "another-long-string-to-be-truncated-and-much-text-here" 个单词用连字符分隔。像:

string

我希望确保最大字符串长度等于30,但我还要确保在达到最大长度时字数不会减半。因此another-long-string-to-be-trun的前30个符号为another-long-string-to-be,但我希望将truncatedexport PATH=$PATH:/usr/local/mysql/bin字完全删除。我怎么能这样做?

5 个答案:

答案 0 :(得分:5)

首先,如果您根本不关心性能,则可以将所有工作转发给正则表达式:

~r/\A(.{0,30})(?:-|\Z)/

我认为这将是最短的解决方案,但效率不高:

iex(28)> string
"another-long-string-to-be-truncated-and-much-text-here"
iex(29)> string2
"another-long-string-to-be-cool-about-that"

iex(30)> Regex.run(~r/\A(.{0,30})(?:-|\Z)/, string) |> List.last() 
"another-long-string-to-be"

iex(31)> Regex.run(~r/\A(.{0,30})(?:-|\Z)/, string2) |> List.last()
"another-long-string-to-be-cool"

有效的解决方案

但是,如果您确实关心性能和内存,那么我建议这样做:

defmodule CoolSlugHelper do
  def slug(input, length \\ 30) do
    length_minus_1 = length - 1

    case input do
      # if the substring ends with "-"
      # i. e. "abc-def-ghi", 8 or "abc-def-", 8 -> "abc-def"
      <<result::binary-size(length_minus_1), "-", _::binary>> -> result

      # if the next char after the substring is "-"
      # i. e. "abc-def-ghi", 7 or "abc-def-", 7 -> "abc-def"
      <<result::binary-size(length), "-", _::binary>> -> result

      # if it is the exact string. i. e. "abc-def", 7 -> "abc-def"
      <<_::binary-size(length)>> -> input

      # return an empty string if we reached the beginnig of the string
      _ when length <= 1 -> ""

      # otherwise look into shorter substring
      _ -> slug(input, length_minus_1)
    end
  end
end

它不按字符收集结果字符串。而是从所需长度到1的范围内寻找正确的子字符串。这就是它在内存和速度方面变得有效的方式。

我们需要此length_minus_1变量,因为我们不能在binary-size二进制模式匹配中使用表达式。

以下是截至2018年12月22日所有拟议解决方案的基准:

(简单的正则表达式是上面的~r/\A(.{0,30})(?:-|\Z)/正则表达式)

Name                     ips        average  deviation         median         99th %
CoolSlugHelper      352.14 K        2.84 μs  ±1184.93%           2 μs           8 μs
SlugHelper           70.98 K       14.09 μs   ±170.20%          10 μs          87 μs
Simple Regex         33.14 K       30.17 μs   ±942.90%          21 μs         126 μs
Truncation           11.56 K       86.51 μs    ±84.81%          62 μs         299 μs

Comparison: 
CoolSlugHelper      352.14 K
SlugHelper           70.98 K - 4.96x slower
Simple Regex         33.14 K - 10.63x slower
Truncation           11.56 K - 30.46x slower

Memory usage statistics:

Name              Memory usage
CoolSlugHelper         2.30 KB
SlugHelper            12.94 KB - 5.61x memory usage
Simple Regex          20.16 KB - 8.75x memory usage
Truncation            35.36 KB - 15.34x memory usage

答案 1 :(得分:4)

UPD 12/2018 Yuri Golobokov发布了更好的解决方案here,我建议使用它而不是以下内容。

最简单的方法是:

"another-long-string-to-be-truncated-and-much-text-here"
|> String.slice(0..29) 
|> String.replace(~r{-[^-]*$}, "")
#⇒ "another-long-string-to-be"

它有一个小故障:如果连字符正好在第31位,则最后一个术语将被删除。为避免这种情况,可以明确检查上述条件:

str = "another-long-string-to-be-truncated-and-much-text-here"
case str |> String.at(30) do                                      
  "-" -> str |> String.slice(0..29)                                  
  _   -> str |> String.slice(0..29) |> String.replace(~r{-[^-]*$}, "")
end                                                               
#⇒ "another-long-string-to-be"

或:

orig = "another-long-string-to-be-cool-cated-and-much-text-here"
str = orig |> String.slice(0..29) 
unless String.at(orig, 30) == "-", do: str = str |> String.replace(~r{-[^-]*$}, "")
str
#⇒ "another-long-string-to-be-cool"

答案 2 :(得分:1)

你可以递归地做..

defmodule Truncation do
  def truncate_words_to(str, max) do
    length = String.length(str)
    words? = Regex.match?(~r{-}, str)
    cond do
      length <= max -> str
      words?        -> truncate_words_to(String.replace(str, ~r{-[^-]*$}, ""),
                                         max)
      true          -> String.slice(str, 0..(max-1))
    end
  end
end

答案 3 :(得分:1)

由于此问题仍然受到搜索引擎的欢迎,因此,我会发布正确,快速,精妙的解决方案来完成此任务。

struct C
{
    C() :second(dummy)
    {

    }
    ~C() // force a destructor
    {

    }
    int * first = nullptr;
    std::vector<int>& second;
};

答案 4 :(得分:0)

我的回答基于@mudasobwa的答案,但我决定简化它

"another-long-string-to-be-truncated-and-much-text-here"
|> String.slice(0..29)
|> String.split("-")
|> Enum.slice(0..-2)
|> Enum.join("-")

就是这样!