printf样式的字符串格式


9

挑战

编写一个实现C printf样式字符串格式的函数。

规则

  1. 必须至少实现%%%c%s%d%f
  2. 不得使用内置的字符串格式化方法。
  3. 不得运行外部程序或从程序连接到Internet。
  4. 由您决定如何处理无效输入,但是您的程序不得异常终止。
  5. 如果可能,您应该编写一个可变参数函数

本文档中的关键字“必须”,“不得”,“必须”,“应”,“应禁止”,“应”,“不应”,“推荐”,“可以”和“可选”是按照RFC 2119中的描述进行解释。


怎么%c办?可以肯定%s%d并且%f分别用于字符串,整数和浮点数,但是不确定%c
Sumurai14年

%c显示传入的int IIRC的ASCII值
marinus

它会打印字符,因此97并且'a'都将出现a在输出中。
nyuszika7h 2014年

不需要支持某种形式的%-02d权利吗?只是那三个%c,%s,%d?
您2014年

@YOU正确。够了
nyuszika7h 2014年

Answers:


4

APL(73)

{⊃,/,⌿↑(⊂2∘↓¨Z⊂G),⊂{'c'0≡⍵,∊⊃⍺:⎕UCS⍺⋄⍕⍺}/⍵,⍪⌷∘G¨1↓1+(Z←G='%')/⍳⍴G←'%!',⍺}

一些测试:

      'a:%c b:%s c:%d'{⊃,/,⌿↑(⊂2∘↓¨Z⊂G),⊂{'c'0≡⍵,∊⊃⍺:⎕UCS⍺⋄⍕⍺}/⍵,⍪⌷∘G¨1↓1+(Z←G='%')/⍳⍴G←'%!',⍺} 65 'foo' 67
a:A b:foo c:67 

      printf←{⊃,/,⌿↑(⊂2∘↓¨Z⊂G),⊂{'c'0≡⍵,∊⊃⍺:⎕UCS⍺⋄⍕⍺}/⍵,⍪⌷∘G¨1↓1+(Z←G='%')/⍳⍴G←'%!',⍺}
      '1:%s 2:%s 3:%d 4:%c 5:%c' printf 'foo' 'bar' 100 110 'z'
1:foo 2:bar 3:100 4:n 5:z   
      'The %s brown %c%c%c jumps over the %s dog.' printf 'quick' 102 111 'x' 'lazy'
The quick brown fox jumps over the lazy dog.

说明:

  • G←'%!',⍺:在字符串前面添加一个虚拟说明符(以便于处理)
  • (Z←G='%')/⍳⍴G:查找%字符串中所有字符的索引;还存储一个位掩码Z
  • ⌷∘G¨1↓1+:选择%s 旁边的所有字符,然后放下假人。
  • ⍵,⍪:将每个说明符与其正确参数中的值进行匹配。
  • {... }/:在每对上运行以下功能:
    • 'c'0≡⍵,∊⊃⍺:如果参数是一个数字并且说明符是c
    • :⎕UCS⍺:然后返回参数的unicode值,
    • ⋄⍕⍺:否则,返回参数的字符串表示形式。
  • :附上
  • ⊂2∘↓¨Z⊂G:在%s 上分割字符串,然后删除每个子字符串的前两个字符(这是虚拟对象所在的位置),并将其结果括起来。
  • :从两个封闭的数组中组成一个矩阵,将每个子字符串与其后面的值匹配。
  • ,⌿:将每个子字符串及其值连接在一起。
  • ⊃,/:然后加入结果字符串。

看到看起来像胡言乱语的神秘语言总是很有趣。;)
nyuszika7h 2014年

2
@ nyuszika7h:这实际上是一种严肃的语言。它的历史可以追溯到1960年代,并且一直在使用。如果不打高尔夫球,看起来会有点像胡言乱语。
marinus 2014年

我知道了,很有趣。
nyuszika7h 2014年

@ nyuszika7h:嗯,从技术上讲,它是面向列表的编程语言,所以可以说它是为代码高尔夫而设计的,尤其是考虑到它使用特殊的字符集来使程序更易读,更省时。这是J编程语言和GolfScript 的灵感来源。
Konrad Borowski14年

@xfix我以为LISP是面向列表的编程语言?我们在大学中使用APL进行实际工作-能够本地处理数组非常方便。J是APL的一位发明者将其设计为“继承人”-当然,这并不意味着它对编码高尔夫没有用...
Jerry Jeremiah

2

Ruby:102个字符

f=->s,*a{s.gsub(/%(.)/){$1==?%??%:a.shift.send({?c=>:chr,?s=>:to_s,?d=>:to_i,?f=>:to_f}[$1])rescue$&}}

样品运行:

irb(main):001:0> f=->s,*a{s.gsub(/%(.)/){$1==?%??%:a.shift.send({?c=>:chr,?s=>:to_s,?d=>:to_i,?f=>:to_f}[$1])rescue$&}}
=> #<Proc:0x96634ac@(irb):1 (lambda)>

irb(main):002:0> puts f["percent : %%\n   char : %c or %c\n string : %s or %s or %s\ndecimal : %d or %d or %d\n  float : %f or %f or %f\ninvalid : %x or %s or %d or %f", 65, 'B', 'format me', 42, Math::PI, 42, Math::PI, '2014', 42, Math::PI, '2014', 'more']
percent : %
   char : A or B
 string : format me or 42 or 3.141592653589793
decimal : 42 or 3 or 2014
  float : 42.0 or 3.141592653589793 or 2014.0
invalid : %x or  or 0 or 0.0
=> nil

无效的格式说明符会保留在原位。没有参数值的格式说明符将替换为给定类型的空值。


您可以提供匿名函数,因此请删除f
cat

确实。但是,正如我记得的那样,在发布本文时,匿名函数并未被一致接受。至于现在,Lua的答案都没有更新为匿名功能(以保存相同数量的字符),我想我将不会启动更新活动。
manatwork'Mar

2

Lua 5.2,115字节

-- Function definition, 115 chars
function f(f,...)n,t=0,{...}return(f:gsub('%%(%a)',function(s)n=n+1return(({c=s.char})[s]or tostring)(t[n])end))end

-- Usage example
print(f('Happy %cew %d %s %f',78,2014,'Year!',math.pi))
-- Output: Happy New 2014 Year! 3.1415926535898

好一个。哪个版本的Lua?5.1.5给出“在'1return'附近的格式错误的数字'”。“%c”的小问题,它在'N'而不是78上失败。还是仅仅是我老Lua的特殊之处?
manatwork 2014年

@manatwork-试试这里
Egor Skriptunoff 2014年

是的,在那里工作。
manatwork 2014年

在Lua 5.2.3上为我工作。
nyuszika7h 2014年

1

C ++(281个字符)

#include<sstream>
#include<cstdarg>
#define q(x)va_arg(v,x);break;case
std::string p(char*f,...){std::ostringstream r;va_list v;va_start(v,f);while(*f)if(*f=='%')switch(++f,*f++){case's':r<<q(char*)'d':r<<q(int)'c':r<<(char)q(int)'%':r<<'%';}else r<<*f++;va_end(v);return r.str();}

我讨厌C ++,但这似乎是一个不错的选择(我真的会选择C,如果不是的话,该char*指针需要付出很多努力才能真正有用)。需要char*参数和std::string结果,但是,嘿,那是C ++,那么谁在乎一致性(用语言本身就是不一致的)?


这没有编译,因为它没有主要功能。
nyuszika7h 2014年

@ nyuszika7h:问题是关于创建函数,而不是main。但是,如果您需要示例main,请尝试gist.github.com/xfix/8238576(我在测试此功能时使用了该示例)。
Konrad Borowski14年

没错,您无法真正实现有意义的main功能,添加一个功能只会增加字符数。如果我不想修改代码,则可以在#include测试程序中添加一个附带的头文件和它。
nyuszika7h 2014年

1

爪哇201个 186 174字节

感谢Kevin Cruijssen 12字节

String f(String s,Object...a){String r="";for(char c,i=0,j=0;i<s.length();r+=c==37?(c=s.charAt(i++))<38?c:c==99?(char)(int)a[j++]:a[j++]:c==37?"":c)c=s.charAt(i++);return r;}

在线尝试!


我不能完全肯定,但我认为你可以删除=s.charAt(0)char c=s.charAt(0)当我这样做时,它仍然可以在TIO中使用。
凯文·克鲁伊森

@KevinCruijssen我发誓这很聪明。
Leaky Nun

我知道已经有一段时间了,但是您可以通过直接打印再保存8个字节:void f(String s,Object...a){for(char c,i=0,j=0;i<s.length();System.out.print(c==37?(c=s.charAt(i++))<38?c:c==99?(char)(int)a[j++]:a[j++]:c==37?"":c))c=s.charAt(i++);} 166个字节(以及通过转换为Java 8 来保存的更多字节,但这不是您的事吗?)
Kevin Cruijssen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.