16
\$\begingroup\$

You are given a string and two characters. You have to print the string between these characters from the string.

Input

Input will first contain a string (not empty or null). In the next line, there will be two characters separated by a space.

Challenge

Return the string between the two characters

Example

Hello! What's your name?
! ?

should result in the output:

" What's your name"

Rules

  • The string will not be longer than 100 characters and will only contain ASCII characters in range (space) to ~(tilde) (character codes 0x20 to 0x7E, inclusive). See ASCII table for reference.
  • You must take input from the stdin( or closest alternative ).
  • The output should be surrounded with quotes(").
  • You can write a full program, or a function which takes input, and outputs the final string
  • The two characters will only contain ASCII characters in range (space) to ~(tilde) (character codes 0x20 to 0x7E, inclusive). See ASCII table for reference.
  • There is no guarantee that both the characters will be in the string.
  • If any of the characters is not found in the string, print "null".
  • If any of the characters is found more than one times (unless both the characters are same) in a string, print "null".
  • If both the characters are the same character, print the string "null".

Test Cases

1)

<HTML>code</HTML>
> <                       --> "null"

2)

What's what?
' '                       --> "null"

3)

abcdefghijklmnopqrstuvwxyz
n k                       --> "lm"

4)

Testing...
e T                       --> ""

5)

Last test-case
  -                       --> "test"

Scoring

This is code golf, so the shortest submission (in bytes) wins.

\$\endgroup\$
18
  • 3
    \$\begingroup\$ Can the characters occur in the opposite order in the string? If so, that could use a test case. \$\endgroup\$ Commented May 18, 2015 at 12:45
  • 1
    \$\begingroup\$ What if the substring contains a "? Should we just surround it with another pair of quotes and not care about that? \$\endgroup\$
    – jimmy23013
    Commented May 18, 2015 at 12:48
  • \$\begingroup\$ @MartinBüttner , Yes. See edited test case 3. Thanks for reminding me about that \$\endgroup\$
    – Spikatrix
    Commented May 18, 2015 at 12:56
  • \$\begingroup\$ @user23013 , Yes. Example input: one"two-three \n" - output: "two" (\n is a newline) \$\endgroup\$
    – Spikatrix
    Commented May 18, 2015 at 12:57
  • 2
    \$\begingroup\$ I'm not a fan of the fiddly details about the markers not appearing or appearing multiple times. I think the problem would be more enjoyable to solve with stronger guarantees on the inputs. \$\endgroup\$
    – xnor
    Commented May 19, 2015 at 5:40

17 Answers 17

3
\$\begingroup\$

CJam, 34 33 32 bytes

'"l_l2%&2*2>NerN/0"null"t_,3=='"

Try it online in the CJam interpreter.

Idea

  1. Remove the second character from line 2.

  2. Form a string consisting of a single copy of all characters that both lines have in common.

  3. Repeat the resulting string twice and discard its first two characters.

    This results in a two-character string (if the characters from line 2 are different and both occur in line 1) or an empty string.

  4. Replace the characters of the resulting string in line 1 by linefeeds.

  5. Split line 1 at linefeeds.

    The resulting array's second element will be the desired string if the array contains exactly three chunks.

  6. Replace the first element of the array with the string null.

  7. Retrieve the second element of the array if its length is 3 and the first otherwise.

  8. Prepend and append a double quote.

Code

'"       e# Push a double quote.
l_       e# Read one line from STDIN. Push a copy.
l2%      e# Read one line from STDIN. Only keep characters at odd indexes.
&        e# Intersect both strings.
2*2>     e# Repeat the intersection twice and discard the first two characters.
Ner      e# Replace the characters of the resulting string with linefeeds.
N/       e# Split the result at linefeeds.
0"null"t e# Replace the first element of the resulting array with "null".
_,3=     e# Push 1 if the length of the array is 3 and 0 otherwise.
=        e# Retrieve the corresponding element from the array.
'"       e# Push a double quote.
\$\endgroup\$
2
\$\begingroup\$

CJam, 38 bytes

l:Tl2%f#_W-$2,=2,@f#$~T<>1>"null"?'"_o

Too long...

Explanation

l:T             e# Read a line and store in T.
l2%             e# Read the two characters into a list.
f#              e# Find each character in the list of two characters.
_W-             e# Copy and remove not found results.
$2,=            e# Sort and check if the result is exactly [0 1].
                e# If true:
2,@f#           e# Find 0 and 1 in the original results.
$               e# Sort.
~T<>            e# Get a slice of T between the two positions (left-closed).
1>              e# Remove the first character.
                e# If false:
"null"          e# The string "null".
?               e# End if.
'"_o            e# Append a quote and output another quote at the beginning.
\$\endgroup\$
0
2
\$\begingroup\$

Pyth, 37 36 34 bytes

p?"null"njT9m/zd{J%2wt:z.uSmxzdJNN

Thanks to @isaacg for the two bytes save.

Try it online: Pyth Compiler/Executor

Explanation:

                                     implicit: z = first input line
                    w                second input line
                  %2                 only use every 2nd char
                 J                   and store in J
                {J                   set(J), gets rid of duplicates
            m/zd                     count the number of appearances of each char
        njT1                         != [1, 1] ([1,1] is 10 in base 9)
 ?      njT1m/zd{J%2w                ... if [1,1] != number of appearances else ...
  "null"                               string "null"
                           mxzdJ     find the index for each char
                          S          sort the indices
                      :z.u           take the substring of z using these indices
                     t               remove the first char

p                               NN  print '"' + ... + '"'
\$\endgroup\$
3
  • \$\begingroup\$ *2]1 is shorter than [1 1), and - ... 1 is shorter still. \$\endgroup\$
    – isaacg
    Commented May 19, 2015 at 16:05
  • \$\begingroup\$ @isaacg -...1 don't work, since I also need to check that there are exactly two numbers. \$\endgroup\$
    – Jakube
    Commented May 19, 2015 at 16:11
  • 2
    \$\begingroup\$ I just thought of a 3 character way to make [1 1): jT9. \$\endgroup\$
    – isaacg
    Commented May 19, 2015 at 16:14
2
\$\begingroup\$

Python 3, 149 bytes

s,i=input(),input();a,b=s.find(i[0]),s.find(i[2]);print('"'+('null',[s[a+1:b],s[b+1:a]][b<a])[(s.count(i[0])==s.count(i[2])==1)*(a!=b)*(a*b>-1)]+'"')

Ungolfed Version:

string, chars = input(), input()
a, b = string.find(chars[0]), string.find(chars[2])

    if string.count(chars[0]) == string.count(chars[2]) == 1 and a!=b and a*b>-1:
        if b<a:
            print('"' + string[b+1:a] + '"')
        else:
            print('"' + string[a+1:b] + '"')
else:
    print('"null"')

This is my first answer here, so tips and criticism are much appreciated.

\$\endgroup\$
0
2
\$\begingroup\$

Ruby, 108 95 94

->s,f,l{a,b=[f,l].map{|u|(f==l||s.count(u)>1)&&abort('"null"');s.index u}.minmax;p s[a+1...b]}

And for the ungolfed version

def between(string, first, last)
    left, right = [first, last].map do |substring|
        abort('"null"') if first == last || string.count(substring) != 1
        string.index(substring)
    end.minmax
    p string[left + 1 ... right]
end
\$\endgroup\$
7
  • \$\begingroup\$ Why can't I see any output when I run your code here? \$\endgroup\$
    – Spikatrix
    Commented May 20, 2015 at 8:47
  • \$\begingroup\$ @CoolGuy It is an unnamed function, so you need to call it like this ->s,f,l{begin a,b=[f,l].map{|u|raise if f==l||s.count(u)>1;s.index u}.minmax;p s[a+1...b];rescue;p'null'end}["<html>test</html>",?>,?<] The [...] at the end is what calls the function. \$\endgroup\$
    – blutorange
    Commented May 20, 2015 at 11:13
  • \$\begingroup\$ @blutorange , Ok. That sort-of worked, but how do I test the last test case? \$\endgroup\$
    – Spikatrix
    Commented May 20, 2015 at 11:17
  • \$\begingroup\$ @CoolGuy Use normally quoted strings: ->s,f,l{begin a,b=[f,l].map{|u|raise if f==l||s.count(u)>1;s.index u}.minmax;p s[a+1...b];rescue;p'null'end}["Last test-case"," ","-"] \$\endgroup\$
    – blutorange
    Commented May 20, 2015 at 11:43
  • \$\begingroup\$ Instead of raising an error with raise, you can replace raise with an undefined variable such as _ or y. This raises a NameError. Also, I think you could save a few more bytes without an explicit rescue: ->s,f,l{a,b=[f,l].map{|u|(f==l||s.count(u)!=1)&&p('null')&&exit;s.index u}.minmax;p s[a+1...b]} \$\endgroup\$
    – blutorange
    Commented May 20, 2015 at 12:03
2
\$\begingroup\$

TypeScript's Type System, 160 bytes

//@ts-ignore
type F<S,U,Y=any,O={[K in U]:S extends`${Y}${K}${infer X}${Exclude<U,K>}${Y}`?S extends`${Y}${K}${Y}${K}${Y}`?0&1:X:0&1}[U]>=O extends 0&1?"null":O

Try it at the TypeScript playground!

This is a generic type F taking the string as the first type parameter S and the characters as a union type U.

If the input was two distinct characters that were guaranteed only to each appear once in the string, this would be 59 bytes:

type F<S,A,B>=S extends`${any}${A}${infer X}${B}${any}`?X:0

My original solution was around 150 bytes before I realized that the program had to check for the characters in either order, adding many bytes. I came up with the following solution at 161 bytes:

//@ts-ignore
type F<S,A,B,Y=any,Z="null">=S extends`${Y}${`${A}${infer X}${B}`|`${B}${infer X}${A}`}${Y}`?S extends{[K in A|B]:`${Y}${K}${Y}${K}${Y}`}[A|B]?Z:X:Z

...but then I thought of the current solution and it ended up exactly 1 byte shorter.

Explanation coming soon.

\$\endgroup\$
1
\$\begingroup\$

C, 192 bytes

f(){char b[101],c,d,*p,*t;scanf("%[^\n]%*c%c%*c%c",b,&c,&d);p=strchr(b,c);t=strchr(b,d);c==d||!p||!t||strchr(p+1,c)||strchr(t+1,d)?puts("\"null\""):printf("\"%s\"",p<t?(*t=0,p+1):(*p=0,t+1));}

Ungolfed code:

f()
{
    char b[101],c,d,*p,*t; //Variables

    scanf("%[^\n]%*c%c%*c%c",b,&c,&d); //Scan input

    p=strchr(b,c);
    t=strchr(b,d); //Find occurrence of characters

    c==d         ||  //If both characters are the same
    !p           ||  //If no occurrence of first character found
    !t           ||  //If no occurrence of second character found
    strchr(p+1,c)||  //If two occurrence of first character found
    strchr(t+1,d) ?  //If two occurrence of second character found
    puts("\"null\"") //Print "null"
                  :  //else
    printf("\"%s\"",p<t?(*t=0,p+1):(*p=0,t+1)); //print the string
}

Test it here

\$\endgroup\$
1
\$\begingroup\$

Python 3, 172 bytes

x=input()
a=input()
a,b=a[0],a[2]
if(a!=b)&(x.count(b)==x.count(a)==1):
 if x.index(a)>x.index(b):q=a;a=b;b=q
 print('"'+x.split(a)[1].split(b)[0]+'"')
else:print('"null"')
\$\endgroup\$
2
  • \$\begingroup\$ Second line can be shortened to “a,_,b=a” because unpacking! \$\endgroup\$
    – Ectogen
    Commented Dec 3, 2023 at 8:03
  • \$\begingroup\$ Also q=a;a=b;b=q is equivalent to a,b=b,a \$\endgroup\$
    – Ectogen
    Commented Dec 3, 2023 at 8:05
1
\$\begingroup\$

Javascript (ES6), 125 123 bytes

Idea stolen heavily from @edc65's solution.

[a,,b]=(p=prompt)(s=p()),[,c,d,e,,f]=s.split(RegExp('(['+(a+b).replace(/\W/g,'\\$&')+'])'))
p('"'+(!e||f||c==e?null:d)+'"')

\$\endgroup\$
2
  • \$\begingroup\$ I mostly like [a,,b]=, I'll use it next time. As regexs are a hassle, here is a regex free solution: [a,,b]=(P=prompt)(s=P()), P((s=s.split(a)).length==2& (s=[].concat(...s.map(s=>s.split(b)))).length==3 ?``"${s[1]}"``:null) \$\endgroup\$
    – edc65
    Commented May 19, 2015 at 8:00
  • \$\begingroup\$ (the last string is templated, difficult to put in a comment) \$\endgroup\$
    – edc65
    Commented May 19, 2015 at 8:02
1
\$\begingroup\$

Python, 161 bytes

import re,sys
s,p=sys.stdin
m=re.match('[^%s]*([%s])([^%s]*)([%s])[^%s]*$'%((p[0]+p[2],)*5),s)
if m:g=m.group
print'"null"'if not m or g(1)==g(3)else'"'+g(2)+'"'

The solution mostly just uses a regular expression to extract the string. To accommodate that the letters could match in either direction, the start and end of the matched part allows either letter. Checking that the letters that actually matched to be different excludes the same letter being matched twice, as well as the two letters in the input being the same.

This is my first attempt at using Python for an answer here. So feedback on possible improvements is very welcome. I particularly have a feeling that there must be a way to make the condition in the print statement shorter.

\$\endgroup\$
1
\$\begingroup\$

Python 3, 155 bytes

s,n,a,b=[input(),'null']+list(input())[::2];q,w=[s.find(a),s.find(b)];print('"'+{0>1:n,0<1:s[min(q,w)+1:max(q,w)],a==b:n}[s.count(a)==s.count(b)==1]+'"')

Try it online

\$\endgroup\$
1
\$\begingroup\$

golflua, 132 bytes

L=I.r()I,J=I.r():m("(.) (.)")i=L:f(I)j=L:f(J)K,c=L:g(I,'')_,b=K:g(J,'')?i>j i,j=j,i$w((i==j|c+b!=2)&'"null"'|'"'..L:s(i+1,j-1)..'"')

Pretty ugly answer. The input bit is a bit rough (and requires two lines, first with the string & second with the slice characters ). Finding the locations of the flags is straight-forward, but just too long to compete with other answers. Output is pretty easy. An equivalent Lua program would be

Line1 = io.read()
Line2 = io.read()
I,J = Line2:match("(.) (.)")     -- boobs return the char flags
i = Line1:find(I)                -- find location of flags
j = Line1:find(J)
K,c = Line1:gsub(I,'')           -- replace flag w/ empty & store in K
_,b = K:gsub(J,'')               -- taking K ensures single flags fail
if i > j then i,j=j,i end        -- ensure we start low to high
if i==j or not (c+b == 2) then   -- if i & j are the same or not 2 counts, fail
   print('"null"')
else                             -- print the string otherwise
   print('"'..Line1:sub(i+1,j-1)..'"')
end
\$\endgroup\$
2
  • \$\begingroup\$ Is there any online compiler which I can test the golfed version? \$\endgroup\$
    – Spikatrix
    Commented May 21, 2015 at 8:29
  • \$\begingroup\$ I don't believe there's an online version, but the source code is available. It is a 1:1 mapping of Lua (not an interpretation or translation to Lua), so the Lua code can be tested at ideone. \$\endgroup\$
    – Kyle Kanos
    Commented May 21, 2015 at 11:08
1
\$\begingroup\$

Vyxal, 164 bitsv2, 20.5 bytes

"`(.*)`wYḂ"ṠẎf:₃[h|`¨β

Try it Online!

Bitstring:

00110000111000011110010000000100111011101000001101101000101001101101100110001100010100001110111101111111011010011000110000000101010010101011001110001001000110010001

Hello to all the people answering this as a result of me bumping it to the front page :p

Explained

"`(.*)`wYḂ"ṠẎf:₃[h|`¨β­⁡​‎‎⁡⁠⁡‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏⁠‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁣⁤‏⁠‏​⁡⁠⁡‌⁢⁢​‎‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏‏​⁡⁠⁡‌⁢⁣​‎‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏⁠‎⁡⁠⁢⁡⁡‏‏​⁡⁠⁡‌⁢⁤​‎‎⁡⁠⁢⁡⁢‏‏​⁡⁠⁡‌⁣⁡​‎‎⁡⁠⁢⁡⁣‏⁠‎⁡⁠⁢⁡⁤‏⁠‎⁡⁠⁢⁢⁡‏⁠‎⁡⁠⁢⁢⁢‏‏​⁡⁠⁡‌­
"                       # ‎⁡Pair the two characters into a list
 `(.*)`                 # ‎⁢Push the string "(.*)", representing a capturing group of any number of characters. The `()` is so that only the .* is returned in a regex match
       wY               # ‎⁣Create a list of [first char, ^, second char]
         Ḃ"             # ‎⁤And wrap it in a list with its reverse
           Ṡ            # ‎⁢⁡Summate each list, giving two regexes: "<first char>(.*)<second char>", "<second char>(.*)<first char>"
            Ẏf          # ‎⁢⁢Get all regex matches of both regexes and flatten into a single list
              :₃[       # ‎⁢⁣If the length is 1 (i.e. there was only one match, meaning the characters aren't the same and there's a unique span):
                 h      # ‎⁢⁤  Print the span
                  |`¨β  # ‎⁣⁡  Otherwise, print "null"
💎

Created with the help of Luminespire.

\$\endgroup\$
0
1
\$\begingroup\$

APL(NARS), 50 chars

{1 1≢↑+/⍵=⊂a←∪1↓1⌽⍺:'"null"'⋄1⌽'""',⍵[↑../⍸⍵∊a]∼a}

the use. For result not sure 100%

  '4 5' {1 1≢↑+/⍵=⊂a←∪1↓1⌽⍺:'"null"'⋄1⌽'""',⍵[↑../⍸⍵∊a]∼a} 'aifdji5ijj4'
"ijj"
\$\endgroup\$
0
\$\begingroup\$

Perl, 65

#!perl -p0
$.*=s/\Q$1/
/g while s/ ?(.)\z//;/
(.*)
/;$_=$.-1?null:"\"$1\""

This requires there is no newline character at in the second line of the input.

\$\endgroup\$
3
  • \$\begingroup\$ Nice job. This seems to be missing the double quotes though. \$\endgroup\$
    – Dennis
    Commented May 19, 2015 at 18:41
  • \$\begingroup\$ @Dennis, fixed. I misunderstood the example. \$\endgroup\$
    – nutki
    Commented May 19, 2015 at 20:11
  • 1
    \$\begingroup\$ It's still missing the quotes for the null case. \$\endgroup\$
    – Dennis
    Commented May 19, 2015 at 20:18
0
\$\begingroup\$

Kotlin, 238 bytes

"\""+v[0].let{s->v[1].let{val(a,b)=it.split("").drop(1).let{c->c[0] to c[2]};if(s.count{""+it==a}!=1||s.count{""+it==b}!=1||a==b)"null" else{val(c,d)= s.indexOf(a) to s.indexOf(b);s.substring((if(c<=d)c else d)+1,if(c<=d)d else c)}}}+"\""

Formatted version:

"\"" + v[0].let { s ->
    v[1].let {
        val (a, b) = it.split("")
            .drop(1)
            .let { c -> c[0] to c[2] } // get first and third char that we are looking fir
        if (s.count { "" + it == a } != 1 || s.count { "" + it == b } != 1 || a == b) { // check if there 0 or more times the character of the first or second char. also check if both are equal
            "null"
        } else {
            val (c, d) = s.indexOf(a) to s.indexOf(b) // fetch indices of thise two chars
            s.substring((if (c <= d) c else d) + 1, if (c <= d) d else c) // build a substring considering the reverse looking
        }
    }
} + "\""

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Scala 3, 192 187 bytes

Saved 5 bytes thanks to @ceilingcat


Golfed version. Attempt This Online!

(s,c)=>{val(a,b)=(s.indexOf(c(0)),s.indexOf(c(2)));if(List(c(0),c(2)).forall(x=>s.count(_==x)==1)&a!=b&a*b> -1)"\""+(if (b<a)s.substring(b+1,a)else s.substring(a+1,b))+"\""else"\"null\""}

Ungolfed version. Attempt This Online!

object Main {
  def findSubstring(string: String, chars: String): String = {
    val a = string.indexOf(chars(0))
    val b = string.indexOf(chars(2))
    
    if (string.count(_ == chars(0)) == 1 && string.count(_ == chars(2)) == 1 && a != b && a * b > -1) {
      if (b < a) {
        "\"" + string.substring(b + 1, a) + "\""
      } else {
        "\"" + string.substring(a + 1, b) + "\""
      }
    } else {
      "\"null\""
    }
  }
  
  def main(args: Array[String]): Unit = {
    val string = scala.io.StdIn.readLine()
    val chars = scala.io.StdIn.readLine()
    println(findSubstring(string, chars))
  }
}
\$\endgroup\$
1

Not the answer you're looking for? Browse other questions tagged or ask your own question.